aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.inc12
-rw-r--r--RELNOTES38
-rw-r--r--UPDATING6
-rw-r--r--bin/sh/histedit.c2
-rw-r--r--bin/timeout/timeout.16
-rw-r--r--contrib/bmake/main.c2
-rw-r--r--contrib/libarchive/NEWS4
-rw-r--r--contrib/libarchive/README.md9
-rw-r--r--contrib/libarchive/cpio/cmdline.c15
-rw-r--r--contrib/libarchive/cpio/cpio.c123
-rw-r--r--contrib/libarchive/cpio/cpio.h2
-rw-r--r--contrib/libarchive/cpio/test/test_format_newc.c7
-rw-r--r--contrib/libarchive/libarchive/archive.h4
-rw-r--r--contrib/libarchive/libarchive/archive_acl.c6
-rw-r--r--contrib/libarchive/libarchive/archive_check_magic.c4
-rw-r--r--contrib/libarchive/libarchive/archive_cryptor_private.h4
-rw-r--r--contrib/libarchive/libarchive/archive_entry.h2
-rw-r--r--contrib/libarchive/libarchive/archive_hmac.c6
-rw-r--r--contrib/libarchive/libarchive/archive_options.c4
-rw-r--r--contrib/libarchive/libarchive/archive_pathmatch.c4
-rw-r--r--contrib/libarchive/libarchive/archive_ppmd8.c22
-rw-r--r--contrib/libarchive/libarchive/archive_ppmd8_private.h25
-rw-r--r--contrib/libarchive/libarchive/archive_read.c12
-rw-r--r--contrib/libarchive/libarchive/archive_read_append_filter.c4
-rw-r--r--contrib/libarchive/libarchive/archive_read_disk_posix.c6
-rw-r--r--contrib/libarchive/libarchive/archive_read_open_filename.c7
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_filter_grzip.c2
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_filter_lz4.c4
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_filter_lzop.c2
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_filter_program.c2
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_7zip.c24
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_cab.c24
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_cpio.c4
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_iso9660.c26
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_lha.c11
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_mtree.c7
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_rar.c73
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_rar5.c45
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_tar.c6
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_xar.c14
-rw-r--r--contrib/libarchive/libarchive/archive_read_support_format_zip.c22
-rw-r--r--contrib/libarchive/libarchive/archive_string.c16
-rw-r--r--contrib/libarchive/libarchive/archive_write.c2
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c22
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c7
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_gzip.c12
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_lrzip.c21
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_lz4.c14
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_lzop.c7
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c22
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_xz.c27
-rw-r--r--contrib/libarchive/libarchive/archive_write_add_filter_zstd.c52
-rw-r--r--contrib/libarchive/libarchive/archive_write_disk_posix.c30
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_7zip.c9
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_cpio_binary.c4
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c2
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_cpio_odc.c2
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_gnutar.c34
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_iso9660.c18
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_mtree.c2
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_pax.c6
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_ustar.c27
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_v7tar.c27
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_xar.c6
-rw-r--r--contrib/libarchive/libarchive/archive_write_set_format_zip.c49
-rw-r--r--contrib/libarchive/libarchive/test/test_acl_text.c23
-rw-r--r--contrib/libarchive/libarchive/test/test_archive_pathmatch.c18
-rw-r--r--contrib/libarchive/libarchive/test/test_archive_string_conversion.c135
-rw-r--r--contrib/libarchive/libarchive/test/test_gnutar_filename_encoding.c40
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_7zip.c26
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_7zip_malformed.c17
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_7zip_malformed3.7z.uu24
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_7zip_sfx_elf64trunc.elf.uu5
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.c45
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.cab.uu11
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.c41
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.cab.uu95
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.c104
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu1096
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.c50
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.lzh.uu60
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.c53
-rw-r--r--contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.rar.uu189
-rw-r--r--contrib/libarchive/libarchive/test/test_read_set_format.c34
-rw-r--r--contrib/libarchive/libarchive/test/test_ustar_filename_encoding.c40
-rw-r--r--contrib/libarchive/libarchive/test/test_v7tar_filename_encoding.c67
-rw-r--r--contrib/libarchive/libarchive/test/test_warn_missing_hardlink_target.c2
-rw-r--r--contrib/libarchive/libarchive/test/test_write_disk.c29
-rw-r--r--contrib/libarchive/libarchive/test/test_write_disk_perms.c11
-rw-r--r--contrib/libarchive/libarchive/test/test_zip_filename_encoding.c40
-rw-r--r--contrib/libarchive/libarchive_fe/lafe_getline.c (renamed from contrib/libarchive/unzip/la_getline.c)11
-rw-r--r--contrib/libarchive/libarchive_fe/lafe_getline.h (renamed from contrib/libarchive/unzip/la_getline.h)15
-rw-r--r--contrib/libarchive/tar/bsdtar.c20
-rw-r--r--contrib/libarchive/tar/read.c2
-rw-r--r--contrib/libarchive/tar/util.c8
-rw-r--r--contrib/libarchive/tar/write.c16
-rw-r--r--contrib/libarchive/test_utils/test_common.h4
-rw-r--r--contrib/libarchive/test_utils/test_main.c85
-rw-r--r--contrib/libarchive/unzip/bsdunzip.c6
-rw-r--r--include/unistd.h5
-rw-r--r--lib/libarchive/tests/Makefile13
-rw-r--r--lib/libc/gen/sysctl.327
-rw-r--r--lib/libcasper/services/cap_dns/cap_dns.c2
-rw-r--r--lib/libifconfig/libifconfig.c4
-rw-r--r--lib/libifconfig/libifconfig_carp.c5
-rw-r--r--lib/libifconfig/libifconfig_sfp.c238
-rw-r--r--lib/libifconfig/libifconfig_sfp.h27
-rw-r--r--lib/libifconfig/sfp.lua120
-rw-r--r--lib/libpfctl/libpfctl.c4
-rw-r--r--lib/libpmc/libpmc_json.cc5
-rw-r--r--lib/libpmc/pmclog.329
-rw-r--r--lib/libpmc/pmclog.c4
-rw-r--r--lib/libpmc/pmclog.h6
-rw-r--r--lib/libsecureboot/h/verify_file.h5
-rw-r--r--lib/libsecureboot/tests/tvo.c6
-rw-r--r--lib/libsecureboot/vectx.c48
-rw-r--r--lib/libsecureboot/verify_file.c3
-rw-r--r--lib/libsys/fork.22
-rw-r--r--lib/libsys/ntp_adjtime.26
-rw-r--r--lib/libsys/x86/pkru.320
-rw-r--r--lib/libthr/libthr.325
-rw-r--r--lib/msun/Makefile16
-rw-r--r--lib/msun/Symbol.map12
-rw-r--r--lib/msun/man/fmax.39
-rw-r--r--lib/msun/man/fmaximum.34
-rw-r--r--lib/msun/man/fmaximum_mag.3102
-rw-r--r--lib/msun/man/fmaximum_num.3113
-rw-r--r--lib/msun/man/math.312
-rw-r--r--lib/msun/src/math.h12
-rw-r--r--lib/msun/src/s_fmaximum_mag.c73
-rw-r--r--lib/msun/src/s_fmaximum_magf.c68
-rw-r--r--lib/msun/src/s_fmaximum_magl.c62
-rw-r--r--lib/msun/src/s_fmaximum_num.c74
-rw-r--r--lib/msun/src/s_fmaximum_numf.c70
-rw-r--r--lib/msun/src/s_fmaximum_numl.c63
-rw-r--r--lib/msun/src/s_fminimum.c2
-rw-r--r--lib/msun/src/s_fminimum_mag.c74
-rw-r--r--lib/msun/src/s_fminimum_magf.c69
-rw-r--r--lib/msun/src/s_fminimum_magl.c63
-rw-r--r--lib/msun/src/s_fminimum_num.c76
-rw-r--r--lib/msun/src/s_fminimum_numf.c71
-rw-r--r--lib/msun/src/s_fminimum_numl.c65
-rw-r--r--lib/msun/src/s_fminimumf.c2
-rw-r--r--lib/msun/src/s_fminimuml.c2
-rw-r--r--lib/msun/tests/fmaximum_fminimum_test.c78
-rw-r--r--libexec/rc/rc.conf2
-rwxr-xr-xlibexec/rc/rc.d/NETWORKING2
-rw-r--r--libexec/rc/rc.d/virtual_oss6
-rw-r--r--libexec/rc/safe_eval.sh13
-rw-r--r--release/Makefile.oracle108
-rw-r--r--release/Makefile.vm5
-rw-r--r--release/packages/ucl/clang.ucl5
-rw-r--r--release/release.conf.sample2
-rw-r--r--release/scripts/oracle/arm64_shape_compatibilities.json24
-rw-r--r--release/scripts/oracle/default_shape_compatibilities.json1
-rwxr-xr-xrelease/scripts/oracle/generate_metadata.lua74
-rw-r--r--release/scripts/oracle/image_capability_data.json96
-rw-r--r--release/scripts/oracle/image_metadata.json21
-rw-r--r--release/tools/ec2-builder.conf1
-rw-r--r--release/tools/ec2-small.conf1
-rw-r--r--release/tools/oracle.conf105
-rw-r--r--sbin/devd/snd.conf14
-rw-r--r--sbin/ifconfig/Makefile5
-rw-r--r--sbin/ifconfig/af_inet6.c4
-rw-r--r--sbin/ifconfig/af_nd6.c4
-rw-r--r--sbin/ifconfig/ifconfig.8113
-rw-r--r--sbin/ifconfig/ifgeneve.c889
-rw-r--r--sbin/ifconfig/sfp.c16
-rw-r--r--sbin/ipf/ippool/ippool.52
-rw-r--r--sbin/pfctl/pfctl.c21
-rw-r--r--sbin/pfctl/pfctl_parser.h6
-rw-r--r--sbin/ping/ping6.c8
-rw-r--r--sbin/tunefs/tunefs.c4
-rw-r--r--secure/lib/libcrypto/Makefile2
-rw-r--r--share/examples/Makefile1
-rw-r--r--share/examples/sound/mmap.c297
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/appleir.493
-rw-r--r--share/man/man4/arcmsr.45
-rw-r--r--share/man/man4/asmc.429
-rw-r--r--share/man/man4/ciss.446
-rw-r--r--share/man/man4/e6000sw.44
-rw-r--r--share/man/man4/geneve.4384
-rw-r--r--share/man/man4/ix.434
-rw-r--r--share/man/man4/nlsysevent.4132
-rw-r--r--share/man/man4/rge.415
-rw-r--r--share/man/man5/rc.conf.557
-rw-r--r--share/man/man5/src.conf.54
-rw-r--r--share/man/man7/freebsd-base.745
-rw-r--r--share/man/man7/tuning.74
-rw-r--r--share/man/man9/Makefile2
-rw-r--r--share/man/man9/OF_getprop.912
-rw-r--r--share/man/man9/hashalloc.9314
-rw-r--r--share/man/man9/hashinit.99
-rw-r--r--share/mk/src.opts.mk1
-rw-r--r--share/skel/dot.profile2
-rw-r--r--share/vt/keymaps/Makefile2
-rw-r--r--stand/common/commands.c2
-rw-r--r--stand/common/load_elf.c7
-rw-r--r--stand/common/load_elf_obj.c5
-rw-r--r--stand/common/module.c16
-rw-r--r--stand/efi/loader/arch/amd64/trap.c88
-rw-r--r--stand/efi/loader/arch/arm/exec.c5
-rw-r--r--stand/efi/loader/arch/arm64/exec.c3
-rw-r--r--stand/efi/loader/arch/riscv/exec.c5
-rw-r--r--stand/efi/loader/main.c8
-rw-r--r--stand/i386/gptzfsboot/zfsboot.c6
-rw-r--r--stand/i386/loader/chain.c4
-rw-r--r--stand/libofw/openfirm.c7
-rw-r--r--stand/libofw/openfirm.h1
-rw-r--r--stand/man/loader.efi.812
-rw-r--r--stand/powerpc/boot1.chrp/boot1.c13
-rw-r--r--stand/powerpc/ofw/ofwfdt.c6
-rw-r--r--sys/amd64/conf/GENERIC3
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2838_xhci.c4
-rw-r--r--sys/arm/broadcom/bcm2835/raspberrypi_virtgpio.c4
-rw-r--r--sys/arm/qualcomm/std.ipq40182
-rw-r--r--sys/arm64/arm64/identcpu.c8
-rw-r--r--sys/arm64/arm64/locore.S20
-rw-r--r--sys/arm64/arm64/pmap.c48
-rw-r--r--sys/arm64/arm64/trap.c22
-rw-r--r--sys/arm64/include/armreg.h189
-rw-r--r--sys/arm64/include/hypervisor.h24
-rw-r--r--sys/arm64/include/vm.h3
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c14
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c2
-rw-r--r--sys/compat/linprocfs/linprocfs.c14
-rw-r--r--sys/compat/linux/linux_ioctl.c115
-rw-r--r--sys/compat/linux/linux_ioctl.h24
-rw-r--r--sys/compat/linux/linux_socket.c126
-rw-r--r--sys/compat/linux/linux_socket.h2
-rw-r--r--sys/compat/linux/linux_stats.c30
-rw-r--r--sys/compat/linuxkpi/common/include/linux/kfifo.h4
-rw-r--r--sys/conf/NOTES7
-rw-r--r--sys/conf/files8
-rw-r--r--sys/conf/files.arm6415
-rw-r--r--sys/conf/kern.pre.mk6
-rw-r--r--sys/conf/ldscript.arm647
-rw-r--r--sys/conf/options1
-rw-r--r--sys/crypto/aesni/aeskeys_i386.S2
-rw-r--r--sys/dev/acpica/acpi_spmc.c11
-rw-r--r--sys/dev/acpica/acpivar.h2
-rw-r--r--sys/dev/asmc/asmc.c718
-rw-r--r--sys/dev/asmc/asmcvar.h49
-rw-r--r--sys/dev/clk/clk_fixed.c8
-rw-r--r--sys/dev/cxgbe/crypto/t6_kern_tls.c6
-rw-r--r--sys/dev/cxgbe/crypto/t7_kern_tls.c2
-rw-r--r--sys/dev/cxgbe/cxgbei/cxgbei.c17
-rw-r--r--sys/dev/cxgbe/cxgbei/icl_cxgbei.c13
-rw-r--r--sys/dev/cxgbe/iw_cxgbe/qp.c24
-rw-r--r--sys/dev/cxgbe/nvmf/nvmf_che.c23
-rw-r--r--sys/dev/cxgbe/tom/t4_connect.c3
-rw-r--r--sys/dev/cxgbe/tom/t4_cpl_io.c44
-rw-r--r--sys/dev/cxgbe/tom/t4_ddp.c18
-rw-r--r--sys/dev/cxgbe/tom/t4_listen.c14
-rw-r--r--sys/dev/cxgbe/tom/t4_tls.c18
-rw-r--r--sys/dev/cxgbe/tom/t4_tom.c10
-rw-r--r--sys/dev/dwc/if_dwc.c8
-rw-r--r--sys/dev/etherswitch/e6000sw/e6000sw.c6
-rw-r--r--sys/dev/etherswitch/e6000sw/e6000swreg.h1
-rw-r--r--sys/dev/hid/appleir.c440
-rw-r--r--sys/dev/hid/bcm5974.c80
-rw-r--r--sys/dev/hid/hid.h10
-rw-r--r--sys/dev/hwpmc/hwpmc_ibs.c6
-rw-r--r--sys/dev/hwpmc/hwpmc_ibs.h2
-rw-r--r--sys/dev/hwpmc/hwpmc_mod.c7
-rw-r--r--sys/dev/iicbus/iic.c39
-rw-r--r--sys/dev/iicbus/iic.h8
-rw-r--r--sys/dev/ixgbe/if_ix.c41
-rw-r--r--sys/dev/ixgbe/if_ix_mdio.c158
-rw-r--r--sys/dev/ixgbe/if_ix_mdio.h34
-rw-r--r--sys/dev/ixgbe/if_ix_mdio_hw.c181
-rw-r--r--sys/dev/ixgbe/if_ix_mdio_hw.h33
-rw-r--r--sys/dev/ixgbe/if_sriov.c2
-rw-r--r--sys/dev/ixgbe/ixgbe.h5
-rw-r--r--sys/dev/ixgbe/ixgbe_common.c4
-rw-r--r--sys/dev/ixgbe/ixgbe_e610.c486
-rw-r--r--sys/dev/ixgbe/ixgbe_e610.h13
-rw-r--r--sys/dev/ixgbe/ixgbe_features.h1
-rw-r--r--sys/dev/ixgbe/ixgbe_fw_logging.c467
-rw-r--r--sys/dev/ixgbe/ixgbe_osdep.c36
-rw-r--r--sys/dev/ixgbe/ixgbe_osdep.h3
-rw-r--r--sys/dev/ixgbe/ixgbe_sriov.h2
-rw-r--r--sys/dev/ixgbe/ixgbe_x540.c16
-rw-r--r--sys/dev/mlx5/mlx5_en/mlx5_en_main.c50
-rw-r--r--sys/dev/nvme/nvme_ctrlr.c50
-rw-r--r--sys/dev/ofw/openfirm.c4
-rw-r--r--sys/dev/ofw/openfirm.h2
-rw-r--r--sys/dev/pci/pcireg.h1
-rw-r--r--sys/dev/qcom_clk/qcom_clk_rcg2.c8
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_clock.c3
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c9
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_main.c22
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_msm8916.h41
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_msm8916_clock.c (renamed from sys/arm64/qualcomm/qcom_gcc.c)83
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_msm8916_reset.c71
-rw-r--r--sys/dev/qcom_gcc/qcom_gcc_var.h1
-rw-r--r--sys/dev/rge/if_rge.c85
-rw-r--r--sys/dev/rge/if_rge_hw.c53
-rw-r--r--sys/dev/rge/if_rge_hw.h1
-rw-r--r--sys/dev/rge/if_rge_sysctl.c5
-rw-r--r--sys/dev/rge/if_rgevar.h1
-rw-r--r--sys/dev/thunderbolt/nhi.c49
-rw-r--r--sys/dev/thunderbolt/nhi_pci.c121
-rw-r--r--sys/dev/thunderbolt/nhi_var.h18
-rw-r--r--sys/dev/thunderbolt/tb_pcib.c12
-rw-r--r--sys/dev/uart/uart_bus_pci.c55
-rw-r--r--sys/dev/uart/uart_dev_ns8250.c9
-rw-r--r--sys/dev/usb/input/wsp.c17
-rw-r--r--sys/dev/usb/net/if_smsc.c2
-rw-r--r--sys/dev/usb/serial/uvscom.c3
-rw-r--r--sys/dev/virtio/block/virtio_blk.c94
-rw-r--r--sys/geom/label/g_label.c4
-rw-r--r--sys/i386/i386/bioscall.S2
-rw-r--r--sys/i386/i386/exception.S2
-rw-r--r--sys/i386/i386/locore.S2
-rw-r--r--sys/i386/i386/mpboot.S2
-rw-r--r--sys/i386/i386/sigtramp.S2
-rw-r--r--sys/i386/i386/support.S2
-rw-r--r--sys/i386/i386/swtch.S2
-rw-r--r--sys/kern/kern_descrip.c38
-rw-r--r--sys/kern/kern_event.c3
-rw-r--r--sys/kern/kern_jail.c1
-rw-r--r--sys/kern/subr_early.c2
-rw-r--r--sys/kern/subr_hash.c404
-rw-r--r--sys/kern/subr_ticks.S2
-rw-r--r--sys/kern/uipc_ktls.c75
-rw-r--r--sys/kern/uipc_socket.c2
-rw-r--r--sys/kern/vfs_aio.c6
-rw-r--r--sys/kern/vfs_syscalls.c5
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/dtb/rockchip/Makefile3
-rw-r--r--sys/modules/hid/Makefile1
-rw-r--r--sys/modules/hid/appleir/Makefile8
-rw-r--r--sys/modules/if_geneve/Makefile7
-rw-r--r--sys/modules/ix/Makefile4
-rw-r--r--sys/modules/ixv/Makefile1
-rw-r--r--sys/net/bpf.c42
-rw-r--r--sys/net/cmis.h450
-rw-r--r--sys/net/if.c55
-rw-r--r--sys/net/if.h6
-rw-r--r--sys/net/if_clone.c17
-rw-r--r--sys/net/if_ethersubr.c39
-rw-r--r--sys/net/if_geneve.c4009
-rw-r--r--sys/net/if_geneve.h70
-rw-r--r--sys/net/if_gif.c21
-rw-r--r--sys/net/if_gif.h4
-rw-r--r--sys/net/if_loop.c10
-rw-r--r--sys/net/if_strings.h12
-rw-r--r--sys/net/iflib.c109
-rw-r--r--sys/net/pfvar.h3
-rw-r--r--sys/net/route/nhop_ctl.c5
-rw-r--r--sys/netinet/icmp6.h6
-rw-r--r--sys/netinet/in_gif.c27
-rw-r--r--sys/netinet/in_mcast.c13
-rw-r--r--sys/netinet/in_pcb.c407
-rw-r--r--sys/netinet/in_pcb.h113
-rw-r--r--sys/netinet/in_pcb_var.h5
-rw-r--r--sys/netinet/ip_carp.c2
-rw-r--r--sys/netinet/ip_carp.h6
-rw-r--r--sys/netinet/ip_fastfwd.c4
-rw-r--r--sys/netinet/ip_input.c41
-rw-r--r--sys/netinet/ip_mroute.c4
-rw-r--r--sys/netinet/raw_ip.c120
-rw-r--r--sys/netinet/sctp_structs.h2
-rw-r--r--sys/netinet/tcp_hpts.c14
-rw-r--r--sys/netinet/tcp_hpts_test.c3
-rw-r--r--sys/netinet/tcp_log_buf.c17
-rw-r--r--sys/netinet/tcp_lro.c110
-rw-r--r--sys/netinet/tcp_output.c1
-rw-r--r--sys/netinet/tcp_stacks/bbr.c2
-rw-r--r--sys/netinet/tcp_stacks/rack.c4
-rw-r--r--sys/netinet/tcp_subr.c14
-rw-r--r--sys/netinet/tcp_syncache.c4
-rw-r--r--sys/netinet/tcp_timer.c10
-rw-r--r--sys/netinet/tcp_timewait.c16
-rw-r--r--sys/netinet/tcp_usrreq.c244
-rw-r--r--sys/netinet/tcp_var.h2
-rw-r--r--sys/netinet/toecore.c10
-rw-r--r--sys/netinet/udp_usrreq.c33
-rw-r--r--sys/netinet6/in6_gif.c27
-rw-r--r--sys/netinet6/in6_pcb.c87
-rw-r--r--sys/netinet6/ip6_fastfwd.c2
-rw-r--r--sys/netinet6/ip6_mroute.c4
-rw-r--r--sys/netinet6/ip6_output.c17
-rw-r--r--sys/netinet6/nd6.c8
-rw-r--r--sys/netinet6/nd6.h20
-rw-r--r--sys/netinet6/nd6_rtr.c449
-rw-r--r--sys/netinet6/udp6_usrreq.c30
-rw-r--r--sys/netipsec/xform_tcp.c8
-rw-r--r--sys/netlink/route/iface.c3
-rw-r--r--sys/netlink/route/interface.h44
-rw-r--r--sys/netpfil/pf/pf.c146
-rw-r--r--sys/netpfil/pf/pf_ioctl.c4
-rw-r--r--sys/netpfil/pf/pf_norm.c8
-rw-r--r--sys/netpfil/pf/pf_table.c2
-rw-r--r--sys/powerpc/booke/pmap.c23
-rw-r--r--sys/powerpc/conf/GENERIC642
-rw-r--r--sys/powerpc/conf/GENERIC64LE2
-rw-r--r--sys/powerpc/include/spr.h5
-rw-r--r--sys/powerpc/include/tlb.h26
-rw-r--r--sys/powerpc/mpc85xx/platform_mpc85xx.c24
-rw-r--r--sys/powerpc/powerpc/intr_machdep.c2
-rw-r--r--sys/sys/fcntl.h6
-rw-r--r--sys/sys/hash.h37
-rw-r--r--sys/sys/kobj.h2
-rw-r--r--sys/sys/mbuf.h6
-rw-r--r--sys/sys/priv.h1
-rw-r--r--sys/sys/signal.h2
-rw-r--r--sys/sys/time.h4
-rw-r--r--sys/tools/syscalls/core/scarg.lua19
-rw-r--r--sys/tools/syscalls/scripts/syscall_json.lua126
-rw-r--r--sys/vm/vm_swapout.c13
-rw-r--r--sys/x86/acpica/acpi_apm.c2
-rw-r--r--sys/x86/cpufreq/hwpstate_amd.c49
-rw-r--r--sys/x86/include/cputypes.h14
-rw-r--r--sys/x86/x86/identcpu.c47
-rw-r--r--tests/ci/tools/ci.conf1
-rw-r--r--tests/sys/arch/Makefile6
-rw-r--r--tests/sys/arch/Makefile.inc2
-rw-r--r--tests/sys/cam/ctl/Makefile2
-rw-r--r--tests/sys/cam/ctl/all-supported-opcodes.txt39
-rw-r--r--tests/sys/cam/ctl/opcodes.sh241
-rw-r--r--tests/sys/fs/fusefs/rename.cc4
-rw-r--r--tests/sys/kqueue/kqueue_fork.c71
-rw-r--r--tests/sys/net/Makefile1
-rw-r--r--tests/sys/net/if_geneve.sh1000
-rw-r--r--tests/sys/netinet/ip_mroute.py153
-rw-r--r--tests/sys/netinet/socket_afinet.c3
-rw-r--r--tests/sys/netpfil/pf/limiters.sh51
-rw-r--r--tools/build/Makefile2
-rw-r--r--tools/build/options/WITH_EXPERIMENTAL1
-rw-r--r--tools/build/test-includes/badfiles.inc1
-rw-r--r--usr.bin/apply/apply.12
-rw-r--r--usr.bin/chpass/chpass.12
-rw-r--r--usr.bin/du/du.14
-rw-r--r--usr.bin/du/du.c50
-rwxr-xr-xusr.bin/du/tests/du_test.sh20
-rw-r--r--usr.bin/fortune/datfiles/freebsd-tips4
-rw-r--r--usr.bin/mkimg/mkimg.c24
-rw-r--r--usr.bin/script/script.14
-rw-r--r--usr.bin/sockstat/sockstat.16
-rw-r--r--usr.bin/unzip/Makefile2
-rw-r--r--usr.bin/yacc/config.h2
-rw-r--r--usr.sbin/adduser/adduser.82
-rw-r--r--usr.sbin/bluetooth/hccontrol/le.c23
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c105
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h2
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c140
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h3
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/iwmbtfw.87
-rw-r--r--usr.sbin/bluetooth/iwmbtfw/main.c80
-rw-r--r--usr.sbin/daemon/daemon.c1
-rw-r--r--usr.sbin/diskinfo/diskinfo.812
-rw-r--r--usr.sbin/diskinfo/diskinfo.c10
-rw-r--r--usr.sbin/efibootmgr/efibootmgr.84
-rw-r--r--usr.sbin/mfiutil/Makefile2
-rw-r--r--usr.sbin/mfiutil/mfi_drive.c59
-rw-r--r--usr.sbin/ndp/Makefile5
-rw-r--r--usr.sbin/ndp/ndp.c7
-rw-r--r--usr.sbin/newsyslog/newsyslog.conf.510
-rw-r--r--usr.sbin/nfsd/nfsv4.4178
-rwxr-xr-xusr.sbin/periodic/etc/daily/404.status-zfs4
-rw-r--r--usr.sbin/pmcstat/pmcstat.826
-rw-r--r--usr.sbin/pmcstat/pmcstat.h5
-rw-r--r--usr.sbin/pmcstat/pmcstat_log.c8
-rw-r--r--usr.sbin/rtadvd/Makefile5
-rw-r--r--usr.sbin/rtadvd/config.c14
-rw-r--r--usr.sbin/rtadvd/rtadvd.c30
-rw-r--r--usr.sbin/rtadvd/rtadvd.h3
-rw-r--r--usr.sbin/virtual_oss/virtual_oss/virtual_oss.813
471 files changed, 20130 insertions, 3373 deletions
diff --git a/Makefile.inc1 b/Makefile.inc1
index 1edab54eeea0..681ebb44fc52 100644
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -2285,6 +2285,8 @@ create-world-package-${pkgname}: .PHONY
/^version/ { print $$2; next } \
' ${WSTAGEDIR}/${pkgname}.ucl && \
${PKG_CMD} -o ABI=${PKG_ABI} -o ALLOW_BASE_SHLIBS=yes \
+ -o SHLIB_PROVIDE_PATHS_NATIVE=/lib,/usr/lib \
+ ${_ALL_LIBCOMPATS:range:@i@-o SHLIB_PROVIDE_PATHS_COMPAT_${_ALL_LIBCOMPATS:[$i]}=/usr/lib${_ALL_libcompats:[$i]}@} \
-o OSVERSION="${SRCRELDATE}" \
create -f ${PKG_FORMAT} ${PKG_CLEVEL} -T${PKG_CTHREADS} \
-M ${WSTAGEDIR}/${pkgname}.ucl \
diff --git a/RELNOTES b/RELNOTES
index b0076bf77bf3..e6e6f07c1d0f 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -10,6 +10,44 @@ newline. Entries should be separated by a newline.
Changes to this file should not be MFCed.
+
+65f5dd42f11c:
+ sh(1) add -l option which makes sh act as a login shell and read the profile.
+
+beab8b1ddf86:
+ bintrans(1) now supports the RFC2047 variant of quoted print
+
+9dc96d8bc3f2:
+ libusb(3) hotplug event are now really hotplugged using either
+ nlsysevent or devd(9) if available.
+
+d64db8892f85:
+ sh(1) is the default shell in the release media.
+
+68ad2b0d7af2:
+ ncurses has been updated to 6.6
+
+52d19df19ed6:
+ nvi(1) has been update to 2.2.2
+
+4d5c434ed16e, 2cfca8e710f2:
+ diff3(1) has replaced GNU diff3 and is now fully compatible.
+
+625dc44832cd:
+ tr(1) is now UTF-8 compliant.
+
+62fba0054d9e:
+ ee(1) is now UTF-8 compliant.
+
+5df6aca10906:
+ ed(1) is now UTF-8 compliant.
+
+28ff4d35f8b9:
+ Update libedit to 2026-04-03.
+
+b42e852e89cb:
+ Add a new pkg-serve(8) to serve packages over TCP via inetd.
+
8b9775912cbc, 53b4ae3bf0f7:
Add support for an NFSv4 root file system.
Note that NFSv3 is still used for bootstrapping. See
diff --git a/UPDATING b/UPDATING
index d4a6e486aed0..c60143c6184e 100644
--- a/UPDATING
+++ b/UPDATING
@@ -27,6 +27,12 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 16.x IS SLOW:
world, or to merely disable the most expensive debugging functionality
at runtime, run "ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20260412:
+ The /etc/rc.d/NETWORKING script no longer provides the legacy
+ NETWORK alias. Third-party or local RC scripts that still use
+ "REQUIRE: NETWORK" shall be updated to use "REQUIRE: NETWORKING"
+ instead.
+
20260129:
The "net.inet6.ip6.use_stableaddr" sysctl is now on by default.
This changes the default algorithm to choose IPv6 SLAAC autogenerated
diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
index c109cf20613d..a9a4837a92e5 100644
--- a/bin/sh/histedit.c
+++ b/bin/sh/histedit.c
@@ -233,7 +233,7 @@ sethistsize(const char *hs)
if (hist != NULL) {
if (hs == NULL || !is_number(hs))
- histsize = 100;
+ histsize = 128;
else
histsize = atoi(hs);
history(hist, &he, H_SETSIZE, histsize);
diff --git a/bin/timeout/timeout.1 b/bin/timeout/timeout.1
index 0a9754a2cc4e..46a5a986cbce 100644
--- a/bin/timeout/timeout.1
+++ b/bin/timeout/timeout.1
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 3, 2025
+.Dd April 11, 2026
.Dt TIMEOUT 1
.Os
.Sh NAME
@@ -269,6 +269,10 @@ The
utility is expected to conform to the
.St -p1003.1-2024
specification.
+.Pp
+The
+.Fl v
+option and long option names are extensions to that specification.
.Sh HISTORY
The
.Nm
diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c
index ace5c729be51..15b028a65241 100644
--- a/contrib/bmake/main.c
+++ b/contrib/bmake/main.c
@@ -1315,7 +1315,7 @@ ReadFirstDefaultMakefile(void)
}
#ifndef MAKE_SAVE_DOLLARS_DEFAULT
-# define MAKE_SAVE_DOLLARS_DEFAULT "yes"
+# define MAKE_SAVE_DOLLARS_DEFAULT "no"
#endif
/*
diff --git a/contrib/libarchive/NEWS b/contrib/libarchive/NEWS
index be14de445b57..d5e9769771e2 100644
--- a/contrib/libarchive/NEWS
+++ b/contrib/libarchive/NEWS
@@ -1,3 +1,7 @@
+Apr 13, 2026: libarchive 3.8.7 released
+
+Mar 10, 2026: libarchive 3.8.6 released
+
Jan 05, 2026: libarchive 3.8.5 released
Dec 01, 2025: libarchive 3.8.4 released
diff --git a/contrib/libarchive/README.md b/contrib/libarchive/README.md
index e9691f1b710b..3009e1b54d18 100644
--- a/contrib/libarchive/README.md
+++ b/contrib/libarchive/README.md
@@ -37,10 +37,13 @@ The top-level directory contains the following information files:
* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL
* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`).
-The following files in the top-level directory are used by the 'configure' script:
+The following files in the top-level directory are related to the 'configure' script and are only needed by maintainers:
-* `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers
-* `Makefile.in`, `config.h.in` - templates used by configure script
+* `configure.ac` - used (by autoconf) to build the configure script and related files
+* `Makefile.am` - used (by automake) to generate Makefile.in
+* `aclocal.m4` - auto-generated file (created by aclocal) used to build the configure script
+* `Makefile.in` - auto-generated template (created by automake) used by the configure script to create Makefile
+* `config.h.in` - auto-generated template (created by autoheader) used by the configure script to create config.h
## Documentation
diff --git a/contrib/libarchive/cpio/cmdline.c b/contrib/libarchive/cpio/cmdline.c
index db06c03c011d..a3d029c65161 100644
--- a/contrib/libarchive/cpio/cmdline.c
+++ b/contrib/libarchive/cpio/cmdline.c
@@ -11,6 +11,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
@@ -347,9 +350,10 @@ owner_parse(const char *spec, struct cpio_owner *owner, const char **errmsg)
owner->gid = pwent->pw_gid;
} else {
char *end;
+ unsigned long val;
errno = 0;
- owner->uid = (int)strtoul(user, &end, 10);
- if (errno || *end != '\0') {
+ val = strtoul(user, &end, 10);
+ if (errno || *end != '\0' || val > (unsigned)INT_MAX) {
snprintf(errbuff, sizeof(errbuff),
"Couldn't lookup user ``%s''", user);
errbuff[sizeof(errbuff) - 1] = '\0';
@@ -357,6 +361,7 @@ owner_parse(const char *spec, struct cpio_owner *owner, const char **errmsg)
*errmsg = errbuff;
return (-1);
}
+ owner->uid = (int)val;
}
free(user);
}
@@ -373,15 +378,17 @@ owner_parse(const char *spec, struct cpio_owner *owner, const char **errmsg)
}
} else {
char *end;
+ unsigned long val;
errno = 0;
- owner->gid = (int)strtoul(g, &end, 10);
- if (errno || *end != '\0') {
+ val = strtoul(g, &end, 10);
+ if (errno || *end != '\0' || val > (unsigned)INT_MAX) {
snprintf(errbuff, sizeof(errbuff),
"Couldn't lookup group ``%s''", g);
errbuff[sizeof(errbuff) - 1] = '\0';
*errmsg = errbuff;
return (-1);
}
+ owner->gid = (int)val;
}
}
return (0);
diff --git a/contrib/libarchive/cpio/cpio.c b/contrib/libarchive/cpio/cpio.c
index 77eefe809f37..6e6c2c3356c0 100644
--- a/contrib/libarchive/cpio/cpio.c
+++ b/contrib/libarchive/cpio/cpio.c
@@ -8,6 +8,8 @@
#include "cpio_platform.h"
+#include "lafe_getline.h"
+
#include <sys/types.h>
#include <archive.h>
#include <archive_entry.h>
@@ -33,6 +35,9 @@
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
@@ -83,7 +88,7 @@ struct name_cache {
static int extract_data(struct archive *, struct archive *);
const char * cpio_i64toa(int64_t);
-static const char *cpio_rename(const char *name);
+static void cpio_rename(struct archive_entry *);
static int entry_to_archive(struct cpio *, struct archive_entry *);
static int file_to_archive(struct cpio *, const char *);
static void free_cache(struct name_cache *cache);
@@ -110,19 +115,16 @@ static void passphrase_free(char *);
int
main(int argc, char *argv[])
{
- static char buff[16384];
struct cpio _cpio; /* Allocated on stack. */
struct cpio *cpio;
struct cpio_owner owner;
const char *errmsg;
char *tptr;
- int opt, t;
+ int opt;
+ long t;
cpio = &_cpio;
memset(cpio, 0, sizeof(*cpio));
- cpio->buff = buff;
- cpio->buff_size = sizeof(buff);
-
#if defined(HAVE_SIGACTION)
{
@@ -204,13 +206,13 @@ main(int argc, char *argv[])
case 'C': /* NetBSD/OpenBSD */
errno = 0;
tptr = NULL;
- t = (int)strtol(cpio->argument, &tptr, 10);
- if (errno || t <= 0 || *(cpio->argument) == '\0' ||
+ t = strtol(cpio->argument, &tptr, 10);
+ if (errno || t <= 0 || t > INT_MAX || *(cpio->argument) == '\0' ||
tptr == NULL || *tptr != '\0') {
lafe_errc(1, 0, "Invalid blocksize: %s",
cpio->argument);
}
- cpio->bytes_per_block = t;
+ cpio->bytes_per_block = (int)t;
break;
case 'c': /* POSIX 1997 */
cpio->format = "odc";
@@ -222,7 +224,7 @@ main(int argc, char *argv[])
if (archive_match_include_pattern_from_file(
cpio->matching, cpio->argument,
cpio->option_null) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(cpio->matching));
break;
case 'F': /* NetBSD/OpenBSD/GNU cpio */
@@ -231,7 +233,7 @@ main(int argc, char *argv[])
case 'f': /* POSIX 1997 */
if (archive_match_exclude_pattern(cpio->matching,
cpio->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(cpio->matching));
break;
case OPTION_GRZIP:
@@ -247,7 +249,7 @@ main(int argc, char *argv[])
cpio->filename = cpio->argument;
break;
case 'i': /* POSIX 1997 */
- if (cpio->mode != '\0')
+ if (cpio->mode != '\0' && cpio->mode != opt)
lafe_errc(1, 0,
"Cannot use both -i and -%c", cpio->mode);
cpio->mode = opt;
@@ -289,13 +291,13 @@ main(int argc, char *argv[])
cpio->filename = cpio->argument;
break;
case 'o': /* POSIX 1997 */
- if (cpio->mode != '\0')
+ if (cpio->mode != '\0' && cpio->mode != opt)
lafe_errc(1, 0,
"Cannot use both -o and -%c", cpio->mode);
cpio->mode = opt;
break;
case 'p': /* POSIX 1997 */
- if (cpio->mode != '\0')
+ if (cpio->mode != '\0' && cpio->mode != opt)
lafe_errc(1, 0,
"Cannot use both -p and -%c", cpio->mode);
cpio->mode = opt;
@@ -316,17 +318,21 @@ main(int argc, char *argv[])
if (owner_parse(cpio->argument, &owner, &errmsg) != 0) {
if (!errmsg)
errmsg = "Error parsing owner";
- lafe_warnc(-1, "%s", errmsg);
+ lafe_warnc(0, "%s", errmsg);
usage();
}
if (owner.uid != -1)
cpio->uid_override = owner.uid;
- if (owner.uname != NULL)
+ if (owner.uname != NULL) {
+ free(cpio->uname_override);
cpio->uname_override = owner.uname;
+ }
if (owner.gid != -1)
cpio->gid_override = owner.gid;
- if (owner.gname != NULL)
+ if (owner.gname != NULL) {
+ free(cpio->gname_override);
cpio->gname_override = owner.gname;
+ }
break;
case 'r': /* POSIX 1997 */
cpio->option_rename = 1;
@@ -409,7 +415,7 @@ main(int argc, char *argv[])
while (*cpio->argv != NULL) {
if (archive_match_include_pattern(cpio->matching,
*cpio->argv) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(cpio->matching));
--cpio->argc;
++cpio->argv;
@@ -427,7 +433,7 @@ main(int argc, char *argv[])
break;
default:
lafe_errc(1, 0,
- "Must specify at least one of -i, -o, or -p");
+ "Must specify one of -i, -o, or -p");
}
archive_match_free(cpio->matching);
@@ -524,7 +530,7 @@ mode_out(struct cpio *cpio)
int r;
if (cpio->option_append)
- lafe_errc(1, 0, "Append mode not yet supported.");
+ lafe_errc(1, 0, "Append mode not yet supported");
cpio->archive_read_disk = archive_read_disk_new();
if (cpio->archive_read_disk == NULL)
@@ -638,7 +644,7 @@ mode_out(struct cpio *cpio)
int64_t blocks =
(archive_filter_bytes(cpio->archive, 0) + 511)
/ 512;
- fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
+ fprintf(stderr, "%lld %s\n", (long long)blocks,
blocks == 1 ? "block" : "blocks");
}
archive_write_free(cpio->archive);
@@ -696,7 +702,6 @@ remove_leading_slash(const char *p)
static int
file_to_archive(struct cpio *cpio, const char *srcpath)
{
- const char *destpath;
struct archive_entry *entry, *spare;
size_t len;
int r;
@@ -738,7 +743,6 @@ file_to_archive(struct cpio *cpio, const char *srcpath)
* pass mode or the name that will go into the archive in
* output mode.
*/
- destpath = srcpath;
if (cpio->destdir) {
len = cpio->destdir_len + strlen(srcpath) + 8;
if (len >= cpio->pass_destpath_alloc) {
@@ -754,15 +758,17 @@ file_to_archive(struct cpio *cpio, const char *srcpath)
}
strcpy(cpio->pass_destpath, cpio->destdir);
strcat(cpio->pass_destpath, remove_leading_slash(srcpath));
- destpath = cpio->pass_destpath;
+ archive_entry_set_pathname(entry, cpio->pass_destpath);
+ } else {
+ archive_entry_set_pathname(entry, srcpath);
}
if (cpio->option_rename)
- destpath = cpio_rename(destpath);
- if (destpath == NULL) {
+ cpio_rename(entry);
+
+ if (archive_entry_pathname(entry) == NULL) {
archive_entry_free(entry);
return (0);
}
- archive_entry_copy_pathname(entry, destpath);
/*
* If we're trying to preserve hardlinks, match them here.
@@ -791,7 +797,6 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry)
const char *destpath = archive_entry_pathname(entry);
const char *srcpath = archive_entry_sourcepath(entry);
int fd = -1;
- ssize_t bytes_read;
int r;
/* Print out the destination name to the user. */
@@ -869,21 +874,23 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry)
exit(1);
if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) {
- bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size);
+ static char buff[16384];
+ ssize_t bytes_read;
+
+ bytes_read = read(fd, buff, sizeof(buff));
while (bytes_read > 0) {
ssize_t bytes_write;
bytes_write = archive_write_data(cpio->archive,
- cpio->buff, bytes_read);
+ buff, bytes_read);
if (bytes_write < 0)
lafe_errc(1, archive_errno(cpio->archive),
"%s", archive_error_string(cpio->archive));
if (bytes_write < bytes_read) {
lafe_warnc(0,
"Truncated write; file may have "
- "grown while being archived.");
+ "grown while being archived");
}
- bytes_read = read(fd, cpio->buff,
- (unsigned)cpio->buff_size);
+ bytes_read = read(fd, buff, sizeof(buff));
}
}
@@ -997,11 +1004,9 @@ mode_in(struct cpio *cpio)
}
if (archive_match_path_excluded(cpio->matching, entry))
continue;
- if (cpio->option_rename) {
- destpath = cpio_rename(archive_entry_pathname(entry));
- archive_entry_set_pathname(entry, destpath);
- } else
- destpath = archive_entry_pathname(entry);
+ if (cpio->option_rename)
+ cpio_rename(entry);
+ destpath = archive_entry_pathname(entry);
if (destpath == NULL)
continue;
if (cpio->verbose)
@@ -1040,7 +1045,7 @@ mode_in(struct cpio *cpio)
if (!cpio->quiet) {
int64_t blocks = (archive_filter_bytes(a, 0) + 511)
/ 512;
- fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
+ fprintf(stderr, "%lld %s\n", (long long)blocks,
blocks == 1 ? "block" : "blocks");
}
archive_read_free(a);
@@ -1125,7 +1130,7 @@ mode_list(struct cpio *cpio)
if (!cpio->quiet) {
int64_t blocks = (archive_filter_bytes(a, 0) + 511)
/ 512;
- fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
+ fprintf(stderr, "%lld %s\n", (long long)blocks,
blocks == 1 ? "block" : "blocks");
}
archive_read_free(a);
@@ -1292,54 +1297,60 @@ mode_pass(struct cpio *cpio, const char *destdir)
* that an input of '.' means the name should be unchanged. GNU cpio
* treats '.' as a literal new name.
*/
-static const char *
-cpio_rename(const char *name)
+void
+cpio_rename(struct archive_entry *entry)
{
- static char buff[1024];
+ char *buff = NULL, *p, *ret = NULL;
FILE *t;
- char *p, *ret;
+ size_t n = 0;
+ ssize_t r;
#if defined(_WIN32) && !defined(__CYGWIN__)
FILE *to;
t = fopen("CONIN$", "r");
if (t == NULL)
- return (name);
+ return;
to = fopen("CONOUT$", "w");
if (to == NULL) {
fclose(t);
- return (name);
+ return;
}
- fprintf(to, "%s (Enter/./(new name))? ", name);
+ fprintf(to, "%s (Enter/./(new name))? ", archive_entry_pathname(entry));
fclose(to);
#else
t = fopen("/dev/tty", "r+");
if (t == NULL)
- return (name);
- fprintf(t, "%s (Enter/./(new name))? ", name);
+ return;
+ fprintf(t, "%s (Enter/./(new name))? ", archive_entry_pathname(entry));
fflush(t);
#endif
- p = fgets(buff, sizeof(buff), t);
+ r = getline(&buff, &n, t);
fclose(t);
- if (p == NULL)
+ if (r < 1)
/* End-of-file is a blank line. */
- return (NULL);
+ goto done;
+ p = buff;
while (*p == ' ' || *p == '\t')
++p;
if (*p == '\n' || *p == '\0')
/* Empty line. */
- return (NULL);
- if (*p == '.' && p[1] == '\n')
+ goto done;
+ if (*p == '.' && p[1] == '\n') {
/* Single period preserves original name. */
- return (name);
+ free(buff);
+ return;
+ }
ret = p;
/* Trim the final newline. */
while (*p != '\0' && *p != '\n')
++p;
/* Overwrite the final \n with a null character. */
*p = '\0';
- return (ret);
+done:
+ archive_entry_set_pathname(entry, ret);
+ free(buff);
}
static void
diff --git a/contrib/libarchive/cpio/cpio.h b/contrib/libarchive/cpio/cpio.h
index 9bc631b544fe..2621a4c3dda3 100644
--- a/contrib/libarchive/cpio/cpio.h
+++ b/contrib/libarchive/cpio/cpio.h
@@ -71,8 +71,6 @@ struct cpio {
/* Work data. */
struct archive *matching;
- char *buff;
- size_t buff_size;
char *ppbuff;
};
diff --git a/contrib/libarchive/cpio/test/test_format_newc.c b/contrib/libarchive/cpio/test/test_format_newc.c
index 33aa16d07a81..9d4e4e9fb674 100644
--- a/contrib/libarchive/cpio/test/test_format_newc.c
+++ b/contrib/libarchive/cpio/test/test_format_newc.c
@@ -6,6 +6,13 @@
*/
#include "test.h"
+#ifdef HAVE_GETEUID
+#define getuid() geteuid()
+#endif
+#ifdef HAVE_GETEGID
+#define getgid() getegid()
+#endif
+
/* Number of bytes needed to pad 'n' to multiple of 'block', assuming
* that 'block' is a power of two. This trick can be more easily
* remembered as -n & (block - 1), but many compilers quite reasonably
diff --git a/contrib/libarchive/libarchive/archive.h b/contrib/libarchive/libarchive/archive.h
index a9d34beb4f5a..41a5440cac75 100644
--- a/contrib/libarchive/libarchive/archive.h
+++ b/contrib/libarchive/libarchive/archive.h
@@ -34,7 +34,7 @@
* assert that ARCHIVE_VERSION_NUMBER >= 2012108.
*/
/* Note: Compiler will complain if this does not match archive_entry.h! */
-#define ARCHIVE_VERSION_NUMBER 3008005
+#define ARCHIVE_VERSION_NUMBER 3008007
#include <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -177,7 +177,7 @@ __LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
-#define ARCHIVE_VERSION_ONLY_STRING "3.8.5"
+#define ARCHIVE_VERSION_ONLY_STRING "3.8.7"
#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
__LA_DECL const char * archive_version_string(void);
diff --git a/contrib/libarchive/libarchive/archive_acl.c b/contrib/libarchive/libarchive/archive_acl.c
index 362e3308f43f..ab601833def6 100644
--- a/contrib/libarchive/libarchive/archive_acl.c
+++ b/contrib/libarchive/libarchive/archive_acl.c
@@ -1256,8 +1256,12 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
tag = 0;
s = field[n].start;
- st = field[n].start + 1;
len = field[n].end - field[n].start;
+ if (len == 0) {
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ st = s + 1;
switch (*s) {
case L'u':
diff --git a/contrib/libarchive/libarchive/archive_check_magic.c b/contrib/libarchive/libarchive/archive_check_magic.c
index 6b8e0c5595f4..b6e1257949e1 100644
--- a/contrib/libarchive/libarchive/archive_check_magic.c
+++ b/contrib/libarchive/libarchive/archive_check_magic.c
@@ -148,14 +148,14 @@ __archive_check_magic(struct archive *a, unsigned int magic,
if (!handle_type) {
errmsg("PROGRAMMER ERROR: Function ");
errmsg(function);
- errmsg(" invoked with invalid archive handle.\n");
+ errmsg(" invoked with invalid archive handle\n");
diediedie();
}
if (a->magic != magic) {
archive_set_error(a, -1,
"PROGRAMMER ERROR: Function '%s' invoked"
- " on '%s' archive object, which is not supported.",
+ " on '%s' archive object, which is not supported",
function,
handle_type);
a->state = ARCHIVE_STATE_FATAL;
diff --git a/contrib/libarchive/libarchive/archive_cryptor_private.h b/contrib/libarchive/libarchive/archive_cryptor_private.h
index 272f2f84b9c9..1f9298ffdc46 100644
--- a/contrib/libarchive/libarchive/archive_cryptor_private.h
+++ b/contrib/libarchive/libarchive/archive_cryptor_private.h
@@ -109,6 +109,10 @@ typedef struct {
#include <nettle/version.h>
#define ARCHIVE_CRYPTOR_USE_NETTLE 1
+#ifndef AES_MAX_KEY_SIZE
+#define AES_MAX_KEY_SIZE AES256_KEY_SIZE
+#endif
+
typedef struct {
#if NETTLE_VERSION_MAJOR < 3
struct aes_ctx ctx;
diff --git a/contrib/libarchive/libarchive/archive_entry.h b/contrib/libarchive/libarchive/archive_entry.h
index b43435692c27..7122a74ed007 100644
--- a/contrib/libarchive/libarchive/archive_entry.h
+++ b/contrib/libarchive/libarchive/archive_entry.h
@@ -28,7 +28,7 @@
#define ARCHIVE_ENTRY_H_INCLUDED
/* Note: Compiler will complain if this does not match archive.h! */
-#define ARCHIVE_VERSION_NUMBER 3008005
+#define ARCHIVE_VERSION_NUMBER 3008007
/*
* Note: archive_entry.h is for use outside of libarchive; the
diff --git a/contrib/libarchive/libarchive/archive_hmac.c b/contrib/libarchive/libarchive/archive_hmac.c
index 210cca70744d..458092f41b29 100644
--- a/contrib/libarchive/libarchive/archive_hmac.c
+++ b/contrib/libarchive/libarchive/archive_hmac.c
@@ -198,6 +198,7 @@ static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
}
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+#include <nettle/version.h>
static int
__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
@@ -216,7 +217,12 @@ __hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
static void
__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
{
+#if NETTLE_VERSION_MAJOR < 4
hmac_sha1_digest(ctx, (unsigned)*out_len, out);
+#else
+ hmac_sha1_digest(ctx, out);
+ *out_len = SHA1_DIGEST_SIZE;
+#endif
}
static void
diff --git a/contrib/libarchive/libarchive/archive_options.c b/contrib/libarchive/libarchive/archive_options.c
index 6e2c0d2a5971..66491bd4183b 100644
--- a/contrib/libarchive/libarchive/archive_options.c
+++ b/contrib/libarchive/libarchive/archive_options.c
@@ -90,7 +90,9 @@ _archive_set_either_option(struct archive *a, const char *m, const char *o, cons
if (r2 == ARCHIVE_FATAL)
return (ARCHIVE_FATAL);
- if (r2 == ARCHIVE_WARN - 1)
+ if (r1 == ARCHIVE_WARN - 1)
+ return r2;
+ if (r2 == ARCHIVE_WARN -1)
return r1;
return r1 > r2 ? r1 : r2;
}
diff --git a/contrib/libarchive/libarchive/archive_pathmatch.c b/contrib/libarchive/libarchive/archive_pathmatch.c
index 19e0889ffe55..db0d2b791adf 100644
--- a/contrib/libarchive/libarchive/archive_pathmatch.c
+++ b/contrib/libarchive/libarchive/archive_pathmatch.c
@@ -202,7 +202,7 @@ pm(const char *p, const char *s, int flags)
if (*p == '\0')
return (1);
while (*s) {
- if (archive_pathmatch(p, s, flags))
+ if (pm(p, s, flags))
return (1);
++s;
}
@@ -307,7 +307,7 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags)
if (*p == L'\0')
return (1);
while (*s) {
- if (archive_pathmatch_w(p, s, flags))
+ if (pm_w(p, s, flags))
return (1);
++s;
}
diff --git a/contrib/libarchive/libarchive/archive_ppmd8.c b/contrib/libarchive/libarchive/archive_ppmd8.c
index 30196d64a9f5..04b1c0c3e165 100644
--- a/contrib/libarchive/libarchive/archive_ppmd8.c
+++ b/contrib/libarchive/libarchive/archive_ppmd8.c
@@ -61,7 +61,7 @@ typedef struct CPpmd8_Node_
#define EMPTY_NODE 0xFFFFFFFF
-void Ppmd8_Construct(CPpmd8 *p)
+static void Ppmd8_Construct(CPpmd8 *p)
{
unsigned i, k, m;
@@ -89,14 +89,14 @@ void Ppmd8_Construct(CPpmd8 *p)
}
}
-void Ppmd8_Free(CPpmd8 *p)
+static void Ppmd8_Free(CPpmd8 *p)
{
free(p->Base);
p->Size = 0;
p->Base = 0;
}
-Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
+static Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
{
if (p->Base == 0 || p->Size != size)
{
@@ -407,7 +407,7 @@ static void RestartModel(CPpmd8 *p)
}
}
-void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
+static void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
{
p->MaxOrder = maxOrder;
p->RestoreMethod = restoreMethod;
@@ -1042,7 +1042,7 @@ static void Rescale(CPpmd8 *p)
p->FoundState = STATS(p->MinContext);
}
-CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
+static CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
{
CPpmd_See *see;
if (p->MinContext->NumStats != 0xFF)
@@ -1078,7 +1078,7 @@ static void NextContext(CPpmd8 *p)
}
}
-void Ppmd8_Update1(CPpmd8 *p)
+static void Ppmd8_Update1(CPpmd8 *p)
{
CPpmd_State *s = p->FoundState;
s->Freq += 4;
@@ -1093,7 +1093,7 @@ void Ppmd8_Update1(CPpmd8 *p)
NextContext(p);
}
-void Ppmd8_Update1_0(CPpmd8 *p)
+static void Ppmd8_Update1_0(CPpmd8 *p)
{
p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);
p->RunLength += p->PrevSuccess;
@@ -1103,7 +1103,7 @@ void Ppmd8_Update1_0(CPpmd8 *p)
NextContext(p);
}
-void Ppmd8_UpdateBin(CPpmd8 *p)
+static void Ppmd8_UpdateBin(CPpmd8 *p)
{
p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));
p->PrevSuccess = 1;
@@ -1111,7 +1111,7 @@ void Ppmd8_UpdateBin(CPpmd8 *p)
NextContext(p);
}
-void Ppmd8_Update2(CPpmd8 *p)
+static void Ppmd8_Update2(CPpmd8 *p)
{
p->MinContext->SummFreq += 4;
if ((p->FoundState->Freq += 4) > MAX_FREQ)
@@ -1127,7 +1127,7 @@ This code is based on:
PPMd var.I (2002): Dmitry Shkarin : Public domain
Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
-Bool Ppmd8_RangeDec_Init(CPpmd8 *p)
+static Bool Ppmd8_RangeDec_Init(CPpmd8 *p)
{
unsigned i;
p->Low = 0;
@@ -1161,7 +1161,7 @@ static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)
#define MASK(sym) ((signed char *)charMask)[sym]
-int Ppmd8_DecodeSymbol(CPpmd8 *p)
+static int Ppmd8_DecodeSymbol(CPpmd8 *p)
{
size_t charMask[256 / sizeof(size_t)];
if (p->MinContext->NumStats != 0)
diff --git a/contrib/libarchive/libarchive/archive_ppmd8_private.h b/contrib/libarchive/libarchive/archive_ppmd8_private.h
index 454b75f41f25..f0493de04623 100644
--- a/contrib/libarchive/libarchive/archive_ppmd8_private.h
+++ b/contrib/libarchive/libarchive/archive_ppmd8_private.h
@@ -83,12 +83,6 @@ typedef struct
UInt16 BinSumm[25][64];
} CPpmd8;
-void Ppmd8_Construct(CPpmd8 *p);
-Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size);
-void Ppmd8_Free(CPpmd8 *p);
-void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod);
-#define Ppmd8_WasAllocated(p) ((p)->Base != NULL)
-
/* ---------- Internal Functions ---------- */
@@ -104,30 +98,11 @@ extern const Byte PPMD8_kExpEscape[16];
#define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats)))
#endif
-void Ppmd8_Update1(CPpmd8 *p);
-void Ppmd8_Update1_0(CPpmd8 *p);
-void Ppmd8_Update2(CPpmd8 *p);
-void Ppmd8_UpdateBin(CPpmd8 *p);
-
#define Ppmd8_GetBinSumm(p) \
&p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \
p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \
p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)]
-CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale);
-
-
-/* ---------- Decode ---------- */
-
-Bool Ppmd8_RangeDec_Init(CPpmd8 *p);
-#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
-int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */
-
-/* ---------- Encode ---------- */
-
-#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }
-void Ppmd8_RangeEnc_FlushData(CPpmd8 *p);
-void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */
typedef struct
{
diff --git a/contrib/libarchive/libarchive/archive_read.c b/contrib/libarchive/libarchive/archive_read.c
index c9b9d5981516..e5f89bdc8772 100644
--- a/contrib/libarchive/libarchive/archive_read.c
+++ b/contrib/libarchive/libarchive/archive_read.c
@@ -171,7 +171,7 @@ static int64_t
client_skip_proxy(struct archive_read_filter *self, int64_t request)
{
if (request < 0)
- __archive_errx(1, "Negative skip requested.");
+ __archive_errx(1, "Negative skip requested");
if (request == 0)
return 0;
@@ -379,7 +379,7 @@ archive_read_set_callback_data2(struct archive *_a, void *client_data,
if (a->client.dataset == NULL)
{
archive_set_error(&a->archive, ENOMEM,
- "No memory.");
+ "No memory");
return ARCHIVE_FATAL;
}
a->client.nodes = 1;
@@ -388,7 +388,7 @@ archive_read_set_callback_data2(struct archive *_a, void *client_data,
if (iindex > a->client.nodes - 1)
{
archive_set_error(&a->archive, EINVAL,
- "Invalid index specified.");
+ "Invalid index specified");
return ARCHIVE_FATAL;
}
a->client.dataset[iindex].data = client_data;
@@ -409,14 +409,14 @@ archive_read_add_callback_data(struct archive *_a, void *client_data,
"archive_read_add_callback_data");
if (iindex > a->client.nodes) {
archive_set_error(&a->archive, EINVAL,
- "Invalid index specified.");
+ "Invalid index specified");
return ARCHIVE_FATAL;
}
p = realloc(a->client.dataset, sizeof(*a->client.dataset)
* (++(a->client.nodes)));
if (p == NULL) {
archive_set_error(&a->archive, ENOMEM,
- "No memory.");
+ "No memory");
return ARCHIVE_FATAL;
}
a->client.dataset = (struct archive_read_data_node *)p;
@@ -625,7 +625,7 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
r1 = archive_read_data_skip(&a->archive);
if (r1 == ARCHIVE_EOF)
archive_set_error(&a->archive, EIO,
- "Premature end-of-file.");
+ "Premature end-of-file");
if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
a->archive.state = ARCHIVE_STATE_FATAL;
return (ARCHIVE_FATAL);
diff --git a/contrib/libarchive/libarchive/archive_read_append_filter.c b/contrib/libarchive/libarchive/archive_read_append_filter.c
index cd88df119906..d578b06fe714 100644
--- a/contrib/libarchive/libarchive/archive_read_append_filter.c
+++ b/contrib/libarchive/libarchive/archive_read_append_filter.c
@@ -104,6 +104,10 @@ archive_read_append_filter(struct archive *_a, int code)
strcpy(str, "lrzip");
r1 = archive_read_support_filter_lrzip(_a);
break;
+ case ARCHIVE_FILTER_GRZIP:
+ strcpy(str, "grzip");
+ r1 = archive_read_support_filter_grzip(_a);
+ break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Invalid filter code specified");
diff --git a/contrib/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libarchive/libarchive/archive_read_disk_posix.c
index 94fa8fef963f..a7176bdd939d 100644
--- a/contrib/libarchive/libarchive/archive_read_disk_posix.c
+++ b/contrib/libarchive/libarchive/archive_read_disk_posix.c
@@ -921,7 +921,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_path_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Failed : %s", archive_error_string(a->matching));
+ "%s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -1035,7 +1035,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_time_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Failed : %s", archive_error_string(a->matching));
+ "%s", archive_error_string(a->matching));
return (r);
}
if (r) {
@@ -1061,7 +1061,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_match_owner_excluded(a->matching, entry);
if (r < 0) {
archive_set_error(&(a->archive), errno,
- "Failed : %s", archive_error_string(a->matching));
+ "%s", archive_error_string(a->matching));
return (r);
}
if (r) {
diff --git a/contrib/libarchive/libarchive/archive_read_open_filename.c b/contrib/libarchive/libarchive/archive_read_open_filename.c
index a910eefcbfd2..9ec1e6c0e808 100644
--- a/contrib/libarchive/libarchive/archive_read_open_filename.c
+++ b/contrib/libarchive/libarchive/archive_read_open_filename.c
@@ -122,13 +122,14 @@ archive_read_open_filenames(struct archive *a, const char **filenames,
archive_clear_error(a);
do
{
+ size_t len;
if (filename == NULL)
filename = "";
- mine = calloc(1,
- sizeof(*mine) + strlen(filename));
+ len = strlen(filename);
+ mine = calloc(1, sizeof(*mine) + len);
if (mine == NULL)
goto no_memory;
- strcpy(mine->filename.m, filename);
+ memcpy(mine->filename.m, filename, len + 1);
mine->block_size = block_size;
mine->fd = -1;
mine->buffer = NULL;
diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_grzip.c b/contrib/libarchive/libarchive/archive_read_support_filter_grzip.c
index 15b6757cb90c..8ec5d1f855cb 100644
--- a/contrib/libarchive/libarchive/archive_read_support_filter_grzip.c
+++ b/contrib/libarchive/libarchive/archive_read_support_filter_grzip.c
@@ -62,7 +62,7 @@ archive_read_support_filter_grzip(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
- if (__archive_read_register_bidder(a, NULL, NULL,
+ if (__archive_read_register_bidder(a, NULL, "grzip",
&grzip_bidder_vtable) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_lz4.c b/contrib/libarchive/libarchive/archive_read_support_filter_lz4.c
index 144572ef2362..acd7f5157997 100644
--- a/contrib/libarchive/libarchive/archive_read_support_filter_lz4.c
+++ b/contrib/libarchive/libarchive/archive_read_support_filter_lz4.c
@@ -363,7 +363,7 @@ lz4_filter_read(struct archive_read_filter *self, const void **p)
case READ_LEGACY_STREAM:
/* Reading a lz4 stream already failed. */
archive_set_error(&self->archive->archive,
- ARCHIVE_ERRNO_MISC, "Invalid sequence.");
+ ARCHIVE_ERRNO_MISC, "Invalid sequence");
return (ARCHIVE_FATAL);
case READ_DEFAULT_BLOCK:
ret = lz4_filter_read_default_stream(self, p);
@@ -377,7 +377,7 @@ lz4_filter_read(struct archive_read_filter *self, const void **p)
break;
default:
archive_set_error(&self->archive->archive,
- ARCHIVE_ERRNO_MISC, "Program error.");
+ ARCHIVE_ERRNO_MISC, "Program error");
return (ARCHIVE_FATAL);
}
diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_lzop.c b/contrib/libarchive/libarchive/archive_read_support_filter_lzop.c
index b0c4bb13e8b3..12ed78c578cd 100644
--- a/contrib/libarchive/libarchive/archive_read_support_filter_lzop.c
+++ b/contrib/libarchive/libarchive/archive_read_support_filter_lzop.c
@@ -110,7 +110,7 @@ archive_read_support_filter_lzop(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
- if (__archive_read_register_bidder(a, NULL, NULL,
+ if (__archive_read_register_bidder(a, NULL, "lzop",
&lzop_bidder_vtable) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
diff --git a/contrib/libarchive/libarchive/archive_read_support_filter_program.c b/contrib/libarchive/libarchive/archive_read_support_filter_program.c
index 2c8e45302d8e..9f187f852df3 100644
--- a/contrib/libarchive/libarchive/archive_read_support_filter_program.c
+++ b/contrib/libarchive/libarchive/archive_read_support_filter_program.c
@@ -149,6 +149,8 @@ archive_read_support_filter_program_signature(struct archive *_a,
if (signature != NULL && signature_len > 0) {
state->signature_len = signature_len;
state->signature = malloc(signature_len);
+ if (state->signature == NULL)
+ goto memerr;
memcpy(state->signature, signature, signature_len);
}
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
index 330d5515dd50..8926ac50a1ed 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
@@ -34,6 +34,9 @@
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
#endif
@@ -80,7 +83,7 @@
/*
* ELF format
*/
-#define ELF_HDR_MIN_LEN 0x3f
+#define ELF_HDR_MIN_LEN 0x40 /* sizeof(Elf64_Ehdr) */
#define ELF_HDR_EI_CLASS_OFFSET 0x04
#define ELF_HDR_EI_DATA_OFFSET 0x05
@@ -855,13 +858,18 @@ find_elf_data_sec(struct archive_read *a)
while (e_shnum > 0) {
name_offset = (*dec32)(h + sec_tbl_offset);
if (name_offset == data_sym_offset) {
+ uint64_t sel_offset;
+
if (format_64) {
- min_addr = (*dec64)(
+ sel_offset = (*dec64)(
h + sec_tbl_offset + 0x18);
} else {
- min_addr = (*dec32)(
+ sel_offset = (*dec32)(
h + sec_tbl_offset + 0x10);
}
+ if (sel_offset > SSIZE_MAX)
+ break;
+ min_addr = (ssize_t)sel_offset;
break;
}
sec_tbl_offset += e_shentsize;
@@ -967,7 +975,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(zip->sconv));
ret = ARCHIVE_WARN;
}
@@ -1573,7 +1581,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
-15 /* Don't check for zlib header */);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Couldn't initialize zlib stream.");
+ "Couldn't initialize zlib stream");
return (ARCHIVE_FAILED);
}
zip->stream_valid = 1;
@@ -1715,7 +1723,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
if (bytes < 0) {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
- "BCJ2 conversion Failed");
+ "BCJ2 conversion failed");
return (ARCHIVE_FAILED);
}
zip->main_stream_bytes_remaining -=
@@ -1769,7 +1777,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
default:
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
- "Decompression failed(%d)",
+ "Decompression failed (%d)",
r);
return (ARCHIVE_FAILED);
}
@@ -1971,7 +1979,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);
if (bytes < 0) {
archive_set_error(&(a->archive),
- ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");
+ ARCHIVE_ERRNO_MISC, "BCJ2 conversion failed");
return (ARCHIVE_FAILED);
}
zip->main_stream_bytes_remaining -=
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cab.c b/contrib/libarchive/libarchive/archive_read_support_format_cab.c
index 63755ef9e579..bf8ac6b1ca77 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_cab.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_cab.c
@@ -383,8 +383,10 @@ archive_read_support_format_cab(struct archive *_a)
NULL,
NULL);
- if (r != ARCHIVE_OK)
+ if (r != ARCHIVE_OK) {
+ archive_wstring_free(&cab->ws);
free(cab);
+ }
return (ARCHIVE_OK);
}
@@ -978,7 +980,7 @@ archive_read_format_cab_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(sconv));
err = ARCHIVE_WARN;
}
@@ -1024,7 +1026,7 @@ archive_read_format_cab_read_data(struct archive_read *a,
*offset = 0;
archive_clear_error(&a->archive);
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Cannot restore this file split in multivolume.");
+ "Cannot restore this file split in multivolume");
return (ARCHIVE_FAILED);
default:
break;
@@ -1173,6 +1175,9 @@ cab_checksum_finish(struct archive_read *a)
l = 4;
if (cab->cfheader.flags & RESERVE_PRESENT)
l += cab->cfheader.cfdata;
+ if (cfdata->memimage == NULL) {
+ return (ARCHIVE_FAILED);
+ }
cfdata->sum_calculated = cab_checksum_cfdata(
cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
if (cfdata->sum_calculated != cfdata->sum) {
@@ -1360,7 +1365,7 @@ cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
return (cab_read_ahead_cfdata_lzx(a, avail));
default: /* Unsupported compression. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported CAB compression : %s",
+ "Unsupported CAB compression: %s",
cab->entry_cffolder->compname);
*avail = ARCHIVE_FAILED;
return (NULL);
@@ -1447,7 +1452,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
-15 /* Don't check for zlib header */);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Can't initialize deflate decompression.");
+ "Can't initialize deflate decompression");
*avail = ARCHIVE_FATAL;
return (NULL);
}
@@ -1667,7 +1672,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
cab->entry_cffolder->compdata);
if (r != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Can't initialize LZX decompression.");
+ "Can't initialize LZX decompression");
*avail = ARCHIVE_FATAL;
return (NULL);
}
@@ -1685,6 +1690,13 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
cab->uncompressed_buffer + cab->xstrm.total_out;
cab->xstrm.avail_out =
cfdata->uncompressed_size - cab->xstrm.total_out;
+
+ if ((size_t)cfdata->uncompressed_size > cab->uncompressed_buffer_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CFDATA uncompressed size");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
d = __archive_read_ahead(a, 1, &bytes_avail);
if (d == NULL) {
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c
index 526096b39f75..43169f61806d 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c
@@ -397,7 +397,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Pathname can't be converted from %s to current locale.",
+ "Pathname can't be converted from %s to current locale",
archive_string_conversion_charset_name(sconv));
r = ARCHIVE_WARN;
}
@@ -426,7 +426,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Linkname can't be converted from %s to "
- "current locale.",
+ "current locale",
archive_string_conversion_charset_name(sconv));
r = ARCHIVE_WARN;
}
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
index 1635228d5071..c69fcd272bcd 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
@@ -1322,7 +1322,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(
iso9660->sconv_utf16be));
@@ -1400,7 +1400,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Linkname cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(
iso9660->sconv_utf16be));
rd_r = ARCHIVE_WARN;
@@ -1663,7 +1663,7 @@ zisofs_read_data(struct archive_read *a,
r = inflateInit(&zisofs->stream);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Can't initialize zisofs decompression.");
+ "Can't initialize zisofs decompression");
return (ARCHIVE_FATAL);
}
zisofs->stream_valid = 1;
@@ -1728,7 +1728,7 @@ zisofs_read_data(struct archive_read *a,
(void)size;/* UNUSED */
(void)offset;/* UNUSED */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "zisofs is not supported on this platform.");
+ "zisofs is not supported on this platform");
return (ARCHIVE_FAILED);
}
@@ -2756,11 +2756,19 @@ parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
{
if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
- /* paged zlib */
- file->pz = 1;
- file->pz_log2_bs = data[3];
- file->pz_uncompressed_size = archive_le32dec(&data[4]);
- }
+ /* paged zlib */
+ file->pz = 1;
+ file->pz_log2_bs = data[3];
+ if (file->pz_log2_bs < 15 || file->pz_log2_bs > 17) {
+ /* TODO: Return an error here instead of silently
+ * disabling zisofs. That requires propagating an
+ * error return through parse_rockridge() and its
+ * callers. */
+ file->pz = 0;
+ return;
+ }
+ file->pz_uncompressed_size = archive_le32dec(&data[4]);
+ }
}
static void
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_lha.c b/contrib/libarchive/libarchive/archive_read_support_format_lha.c
index cf6a147abda6..ff6dbb8df1cd 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_lha.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_lha.c
@@ -613,7 +613,7 @@ archive_read_format_lha_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to Unicode.",
+ "from %s to Unicode",
archive_string_conversion_charset_name(lha->sconv_dir));
err = ARCHIVE_FATAL;
} else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
@@ -634,7 +634,7 @@ archive_read_format_lha_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to Unicode.",
+ "from %s to Unicode",
archive_string_conversion_charset_name(lha->sconv_fname));
err = ARCHIVE_FATAL;
}
@@ -1101,6 +1101,13 @@ lha_read_file_header_3(struct archive_read *a, struct lha *lha)
header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
__archive_read_consume(a, H3_FIXED_SIZE);
+ /* Reject rediculously large header */
+ if (lha->header_size > 65536) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "LHa header size too large");
+ return (ARCHIVE_FATAL);
+ }
+
/* Read extended headers */
err = lha_read_file_extended_header(a, lha, &header_crc, 4,
lha->header_size - H3_FIXED_SIZE, &extdsize);
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
index 10c07b05d965..4a5a49ca8136 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
@@ -300,7 +300,12 @@ cleanup(struct archive_read *a)
struct mtree_entry *p, *q;
mtree = (struct mtree *)(a->format->data);
-
+
+ /* Close any dangling file descriptor before freeing */
+ if (mtree->fd >= 0) {
+ close(mtree->fd);
+ mtree->fd = -1;
+ }
p = mtree->entries;
while (p != NULL) {
q = p->next;
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libarchive/libarchive/archive_read_support_format_rar.c
index 9b401c00ba34..0ed2540cd0a8 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_rar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_rar.c
@@ -955,7 +955,7 @@ archive_read_format_rar_read_header(struct archive_read *a,
if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to read next header.");
+ "Failed to read next header");
return (ARCHIVE_FATAL);
}
p = h;
@@ -1005,7 +1005,7 @@ archive_read_format_rar_read_header(struct archive_read *a,
archive_entry_set_is_data_encrypted(entry, 1);
rar->has_encrypted_entries = 1;
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "RAR encryption support unavailable.");
+ "RAR encryption support unavailable");
return (ARCHIVE_FATAL);
}
@@ -1141,7 +1141,7 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported compression method for RAR file.");
+ "Unsupported compression method for RAR file");
ret = ARCHIVE_FATAL;
break;
}
@@ -1432,14 +1432,14 @@ read_header(struct archive_read *a, struct archive_entry *entry,
else
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "RAR solid archive support unavailable.");
+ "RAR solid archive support unavailable");
return (ARCHIVE_FATAL);
}
if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to read full header content.");
+ "Failed to read full header content");
return (ARCHIVE_FATAL);
}
@@ -1471,7 +1471,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
archive_entry_set_is_data_encrypted(entry, 1);
rar->has_encrypted_entries = 1;
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "RAR encryption support unavailable.");
+ "RAR encryption support unavailable");
/* Since it is only the data part itself that is encrypted we can at least
extract information about the currently processed entry and don't need
to return ARCHIVE_FATAL here. */
@@ -1503,7 +1503,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
if (rar->packed_size < 0 || rar->unp_size < 0)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Invalid sizes specified.");
+ "Invalid sizes specified");
return (ARCHIVE_FATAL);
}
@@ -1516,19 +1516,19 @@ read_header(struct archive_read *a, struct archive_entry *entry,
size_t distance = p - (const char *)h;
if (rar->packed_size > INT64_MAX - header_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Extended header size too large.");
+ "Extended header size too large");
return (ARCHIVE_FATAL);
}
header_size += rar->packed_size;
if ((uintmax_t)header_size > SIZE_MAX) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unable to read extended header data.");
+ "Unable to read extended header data");
return (ARCHIVE_FATAL);
}
/* Make sure we have the extended data. */
if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to read extended header data.");
+ "Failed to read extended header data");
return (ARCHIVE_FATAL);
}
p = h;
@@ -1547,7 +1547,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
newptr = realloc(rar->filename, newsize);
if (newptr == NULL) {
archive_set_error(&a->archive, ENOMEM,
- "Couldn't allocate memory.");
+ "Couldn't allocate memory");
return (ARCHIVE_FATAL);
}
rar->filename = newptr;
@@ -1701,7 +1701,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
newsize = sizeof(*rar->dbo) * (rar->nodes + 1);
if ((newdbo = realloc(rar->dbo, newsize)) == NULL)
{
- archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory");
return (ARCHIVE_FATAL);
}
rar->dbo = newdbo;
@@ -1715,7 +1715,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
if (rar->packed_size > INT64_MAX - a->filter->position)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unable to store offsets.");
+ "Unable to store offsets");
return (ARCHIVE_FATAL);
}
rar->dbo[rar->cursor].start_offset = a->filter->position;
@@ -1734,7 +1734,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
newsize = filename_size + 1;
if ((newptr = realloc(rar->filename_save, newsize)) == NULL)
{
- archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory");
return (ARCHIVE_FATAL);
}
rar->filename_save = newptr;
@@ -1745,7 +1745,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
free(rar->dbo);
if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)
{
- archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory");
return (ARCHIVE_FATAL);
}
rar->dbo[0].header_size = header_size;
@@ -1776,7 +1776,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
__archive_read_consume(a, header_size - 7);
if (rar->packed_size > INT64_MAX - a->filter->position) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unable to store offsets.");
+ "Unable to store offsets");
return (ARCHIVE_FATAL);
}
rar->dbo[0].start_offset = a->filter->position;
@@ -1848,7 +1848,7 @@ read_header(struct archive_read *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Pathname cannot be converted from %s to current locale.",
+ "Pathname cannot be converted from %s to current locale",
archive_string_conversion_charset_name(fn_sconv));
ret = (ARCHIVE_WARN);
}
@@ -1979,13 +1979,13 @@ read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
if ((uintmax_t)rar->packed_size > SIZE_MAX)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unable to read link.");
+ "Unable to read link");
return (ARCHIVE_FATAL);
}
if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to read link.");
+ "Failed to read link");
return (ARCHIVE_FATAL);
}
p = h;
@@ -2000,7 +2000,7 @@ read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "link cannot be converted from %s to current locale.",
+ "link cannot be converted from %s to current locale",
archive_string_conversion_charset_name(sconv));
ret = (ARCHIVE_WARN);
}
@@ -2201,7 +2201,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
case 3:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Parsing filters is unsupported.");
+ "Parsing filters is unsupported");
return (ARCHIVE_FAILED);
case 4:
@@ -2473,7 +2473,7 @@ parse_codes(struct archive_read *a)
free(precode.tree);
free(precode.table);
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Internal error extracting RAR file.");
+ "Internal error extracting RAR file");
return (ARCHIVE_FATAL);
}
@@ -2548,7 +2548,8 @@ parse_codes(struct archive_read *a)
return (r);
}
- if (!rar->dictionary_size || !rar->lzss.window)
+ if (!rar->dictionary_size || !rar->lzss.window ||
+ (unsigned int)(rar->lzss.mask + 1) < rar->dictionary_size)
{
/* Seems as though dictionary sizes are not used. Even so, minimize
* memory usage as much as possible.
@@ -2562,13 +2563,13 @@ parse_codes(struct archive_read *a)
new_size = rar_fls((unsigned int)rar->unp_size) << 1;
if (new_size == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Zero window size is invalid.");
+ "Zero window size is invalid");
return (ARCHIVE_FATAL);
}
new_window = realloc(rar->lzss.window, new_size);
if (new_window == NULL) {
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for uncompressed data.");
+ "Unable to allocate memory for uncompressed data");
return (ARCHIVE_FATAL);
}
rar->lzss.window = (unsigned char *)new_window;
@@ -2686,7 +2687,7 @@ create_code(struct archive_read *a, struct huffman_code *code,
code->numallocatedentries = 0;
if (new_node(code) < 0) {
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for node data.");
+ "Unable to allocate memory for node data");
return (ARCHIVE_FATAL);
}
code->numentries = 1;
@@ -2769,12 +2770,12 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
if ((repeatnode = new_node(code)) < 0) {
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for node data.");
+ "Unable to allocate memory for node data");
return (ARCHIVE_FATAL);
}
if ((nextnode = new_node(code)) < 0) {
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for node data.");
+ "Unable to allocate memory for node data");
return (ARCHIVE_FATAL);
}
@@ -2794,7 +2795,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
{
if (new_node(code) < 0) {
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for node data.");
+ "Unable to allocate memory for node data");
return (ARCHIVE_FATAL);
}
code->tree[lastnode].branches[bit] = code->numentries++;
@@ -2863,13 +2864,13 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
if (!code->tree)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Huffman tree was not created.");
+ "Huffman tree was not created");
return (ARCHIVE_FATAL);
}
if (node < 0 || node >= code->numentries)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Invalid location to Huffman tree specified.");
+ "Invalid location to Huffman tree specified");
return (ARCHIVE_FATAL);
}
@@ -3149,6 +3150,11 @@ copy_from_lzss_window(struct archive_read *a, uint8_t *buffer,
windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
firstpart = lzss_size(&rar->lzss) - windowoffs;
+ if (length > lzss_size(&rar->lzss)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
if (firstpart < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Bad RAR file data");
@@ -3180,7 +3186,7 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)
{
archive_set_error(&a->archive, ENOMEM,
- "Unable to allocate memory for uncompressed data.");
+ "Unable to allocate memory for uncompressed data");
return (ARCHIVE_FATAL);
}
}
@@ -3315,7 +3321,8 @@ parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint
else
blocklength = prog ? prog->oldfilterlength : 0;
- if (blocklength > rar->dictionary_size)
+ if (blocklength > rar->dictionary_size ||
+ blocklength > (uint32_t)(rar->lzss.mask + 1))
return 0;
registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS;
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar5.c b/contrib/libarchive/libarchive/archive_read_support_format_rar5.c
index 17e501e02e9f..63dd97b3008a 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_rar5.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_rar5.c
@@ -375,6 +375,7 @@ static int rar5_read_data_skip(struct archive_read *a);
static int push_data_ready(struct archive_read* a, struct rar5* rar,
const uint8_t* buf, size_t size, int64_t offset);
static void clear_data_ready_stack(struct rar5* rar);
+static void rar5_deinit(struct rar5* rar);
/* CDE_xxx = Circular Double Ended (Queue) return values. */
enum CDE_RETURN_VALUES {
@@ -429,8 +430,7 @@ static int cdeque_front(struct cdeque* d, void** value) {
return CDE_OUT_OF_BOUNDS;
}
-/* Pushes a new element into the end of this circular deque object. If current
- * size will exceed capacity, the oldest element will be overwritten. */
+/* Pushes a new element into the end of this circular deque object. */
static int cdeque_push_back(struct cdeque* d, void* item) {
if(d == NULL)
return CDE_PARAM;
@@ -554,7 +554,11 @@ static struct filter_info* add_new_filter(struct rar5* rar) {
return NULL;
}
- cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
+ if (CDE_OK != cdeque_push_back(&rar->cstate.filters, cdeque_filter(f))) {
+ free(f);
+ return NULL;
+ }
+
return f;
}
@@ -671,7 +675,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
rar->cstate.filtered_buf = malloc(flt->block_length);
if(!rar->cstate.filtered_buf) {
archive_set_error(&a->archive, ENOMEM,
- "Can't allocate memory for filter data.");
+ "Can't allocate memory for filter data");
return ARCHIVE_FATAL;
}
@@ -1847,7 +1851,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
rar->cstate.window_buf == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Declared solid file, but no window buffer "
- "initialized yet.");
+ "initialized yet");
return ARCHIVE_FATAL;
}
@@ -1857,7 +1861,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
(rar->file.dir == 0 && window_size == 0))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Declared dictionary size is not supported.");
+ "Declared dictionary size is not supported");
return ARCHIVE_FATAL;
}
@@ -1869,7 +1873,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Window size for this solid file doesn't match "
- "the window size used in previous solid file. ");
+ "the window size used in previous solid file");
return ARCHIVE_FATAL;
}
}
@@ -1895,7 +1899,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
if(!new_window_buf) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Not enough memory when trying to realloc the window "
- "buffer.");
+ "buffer");
return ARCHIVE_FATAL;
}
@@ -3040,7 +3044,9 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
if(block_length < 4 ||
block_length > 0x400000 ||
filter_type > FILTER_ARM ||
- !is_valid_filter_block_start(rar, block_start))
+ !is_valid_filter_block_start(rar, block_start) ||
+ (rar->cstate.window_size > 0 &&
+ (ssize_t)block_length > rar->cstate.window_size >> 1))
{
archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid filter encountered");
@@ -3051,7 +3057,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
filt = add_new_filter(rar);
if(filt == NULL) {
archive_set_error(&ar->archive, ENOMEM,
- "Can't allocate memory for a filter descriptor.");
+ "Can't allocate memory for a filter descriptor");
return ARCHIVE_FATAL;
}
@@ -3500,7 +3506,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
rar->vol.push_buf = malloc(block_size + 8);
if(!rar->vol.push_buf) {
archive_set_error(&a->archive, ENOMEM,
- "Can't allocate memory for a merge block buffer.");
+ "Can't allocate memory for a merge block buffer");
return ARCHIVE_FATAL;
}
@@ -3533,7 +3539,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
if(partial_offset + cur_block_size > block_size) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
- "Consumed too much data when merging blocks.");
+ "Consumed too much data when merging blocks");
return ARCHIVE_FATAL;
}
@@ -3802,7 +3808,7 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar,
* as an internal error. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Error: premature end of data_ready stack");
+ "Premature end of data_ready stack");
return ARCHIVE_FATAL;
}
@@ -4328,7 +4334,7 @@ static int rar5_cleanup(struct archive_read *a) {
free(rar->vol.push_buf);
free_filters(rar);
- cdeque_free(&rar->cstate.filters);
+ rar5_deinit(rar);
free(rar);
a->format->data = NULL;
@@ -4353,6 +4359,7 @@ static int rar5_has_encrypted_entries(struct archive_read *_a) {
return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
}
+/* Must match deallocations in rar5_deinit */
static int rar5_init(struct rar5* rar) {
memset(rar, 0, sizeof(struct rar5));
@@ -4368,6 +4375,11 @@ static int rar5_init(struct rar5* rar) {
return ARCHIVE_OK;
}
+/* Must match allocations in rar5_init */
+static void rar5_deinit(struct rar5* rar) {
+ cdeque_free(&rar->cstate.filters);
+}
+
int archive_read_support_format_rar5(struct archive *_a) {
struct archive_read* ar;
int ret;
@@ -4404,8 +4416,9 @@ int archive_read_support_format_rar5(struct archive *_a) {
rar5_has_encrypted_entries);
if(ret != ARCHIVE_OK) {
- (void) rar5_cleanup(ar);
+ rar5_deinit(rar);
+ free(rar);
}
- return ret;
+ return ARCHIVE_OK;
}
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libarchive/libarchive/archive_read_support_format_tar.c
index 98f7d699570a..2979492767ed 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_tar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_tar.c
@@ -1202,7 +1202,7 @@ set_conversion_failed_error(struct archive_read *a,
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "%s can't be converted from %s to current locale.",
+ "%s can't be converted from %s to current locale",
name, archive_string_conversion_charset_name(sconv));
return (ARCHIVE_WARN);
}
@@ -2255,12 +2255,12 @@ pax_attribute_SCHILY_acl(struct archive_read *a, struct tar *tar,
if (r != ARCHIVE_OK) {
if (r == ARCHIVE_FATAL) {
archive_set_error(&a->archive, ENOMEM,
- "%s %s", "Can't allocate memory for ",
+ "%s %s", "Can't allocate memory for",
errstr);
return (r);
}
archive_set_error(&a->archive,
- ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+ ARCHIVE_ERRNO_MISC, "%s %s", "Parse error:", errstr);
}
return (r);
}
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libarchive/libarchive/archive_read_support_format_xar.c
index 36b5ab3ae04c..874501fc0782 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_xar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_xar.c
@@ -733,7 +733,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
- "Gname cannot be converted from %s to current locale.",
+ "Gname cannot be converted from %s to current locale",
archive_string_conversion_charset_name(xar->sconv));
r = ARCHIVE_WARN;
}
@@ -748,7 +748,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
- "Uname cannot be converted from %s to current locale.",
+ "Uname cannot be converted from %s to current locale",
archive_string_conversion_charset_name(xar->sconv));
r = ARCHIVE_WARN;
}
@@ -762,7 +762,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
- "Pathname cannot be converted from %s to current locale.",
+ "Pathname cannot be converted from %s to current locale",
archive_string_conversion_charset_name(xar->sconv));
r = ARCHIVE_WARN;
}
@@ -778,7 +778,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
- "Linkname cannot be converted from %s to current locale.",
+ "Linkname cannot be converted from %s to current locale",
archive_string_conversion_charset_name(xar->sconv));
r = ARCHIVE_WARN;
}
@@ -1008,7 +1008,7 @@ move_reading_point(struct archive_read *a, uint64_t offset)
if (pos == ARCHIVE_FAILED) {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
- "Cannot seek.");
+ "Cannot seek");
return (ARCHIVE_FAILED);
}
xar->offset = pos;
@@ -1476,7 +1476,7 @@ decompression_init(struct archive_read *a, enum enctype encoding)
r = inflateInit(&(xar->stream));
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Couldn't initialize zlib stream.");
+ "Couldn't initialize zlib stream");
return (ARCHIVE_FATAL);
}
xar->stream_valid = 1;
@@ -1691,7 +1691,7 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes,
default:
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
- "%s decompression failed(%d)",
+ "%s decompression failed (%d)",
(xar->entry_encoding == XZ)?"xz":"lzma",
r);
return (ARCHIVE_FATAL);
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libarchive/libarchive/archive_read_support_format_zip.c
index 0c86ce935e26..00796b288a48 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_zip.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_zip.c
@@ -1008,7 +1008,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(sconv));
ret = ARCHIVE_WARN;
}
@@ -1256,7 +1256,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Symlink cannot be converted "
- "from %s to current locale.",
+ "from %s to current locale",
archive_string_conversion_charset_name(
sconv));
ret = ARCHIVE_WARN;
@@ -1726,7 +1726,7 @@ zipx_xz_init(struct archive_read *a, struct zip *zip)
r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0);
if (r != LZMA_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
- "xz initialization failed(%d)",
+ "xz initialization failed (%d)",
r);
return (ARCHIVE_FAILED);
@@ -1778,7 +1778,7 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX);
if (r != LZMA_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
- "lzma initialization failed(%d)", r);
+ "lzma initialization failed (%d)", r);
return (ARCHIVE_FAILED);
}
@@ -1921,7 +1921,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
switch(lz_ret) {
case LZMA_DATA_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "xz data error (error %d)", (int) lz_ret);
+ "xz data error (%d)", (int) lz_ret);
return (ARCHIVE_FATAL);
case LZMA_NO_CHECK:
@@ -1930,7 +1930,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "xz unknown error %d", (int) lz_ret);
+ "xz unknown error (%d)", (int) lz_ret);
return (ARCHIVE_FATAL);
case LZMA_STREAM_END:
@@ -2018,7 +2018,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
switch(lz_ret) {
case LZMA_DATA_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "lzma data error (error %d)", (int) lz_ret);
+ "lzma data error (%d)", (int) lz_ret);
return (ARCHIVE_FATAL);
/* This case is optional in lzma alone format. It can happen,
@@ -2041,7 +2041,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "lzma unknown error %d", (int) lz_ret);
+ "lzma unknown error (%d)", (int) lz_ret);
return (ARCHIVE_FATAL);
}
@@ -2261,7 +2261,7 @@ zipx_bzip2_init(struct archive_read *a, struct zip *zip)
r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);
if(r != BZ_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
- "bzip2 initialization failed(%d)",
+ "bzip2 initialization failed (%d)",
r);
return ARCHIVE_FAILED;
@@ -2527,7 +2527,7 @@ zip_deflate_init(struct archive_read *a, struct zip *zip)
-15 /* Don't check for zlib header */);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Can't initialize ZIP decompression.");
+ "Can't initialize ZIP decompression");
return (ARCHIVE_FATAL);
}
/* Stream structure has been set up. */
@@ -3193,7 +3193,7 @@ archive_read_format_zip_read_data(struct archive_read *a,
!= (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"ZIP uncompressed data is wrong size "
- "(read %jd, expected %jd)\n",
+ "(read %jd, expected %jd)",
(intmax_t)zip->entry_uncompressed_bytes_read,
(intmax_t)zip->entry->uncompressed_size);
return (ARCHIVE_FAILED);
diff --git a/contrib/libarchive/libarchive/archive_string.c b/contrib/libarchive/libarchive/archive_string.c
index a776dc85c688..c6ae8968d54f 100644
--- a/contrib/libarchive/libarchive/archive_string.c
+++ b/contrib/libarchive/libarchive/archive_string.c
@@ -772,7 +772,7 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as,
int r;
defchar_used = 0;
- if (to_cp == CP_UTF8 || sc == NULL)
+ if (to_cp == CP_UTF8)
dp = NULL;
else
dp = &defchar_used;
@@ -1713,7 +1713,7 @@ get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
if (a != NULL) {
#if HAVE_ICONV
archive_set_error(a, ARCHIVE_ERRNO_MISC,
- "iconv_open failed : Cannot handle ``%s''",
+ "iconv_open failed: Cannot handle ``%s''",
(flag & SCONV_TO_CHARSET)?tc:fc);
#else
archive_set_error(a, ARCHIVE_ERRNO_MISC,
@@ -1873,6 +1873,9 @@ archive_string_conversion_free(struct archive *a)
const char *
archive_string_conversion_charset_name(struct archive_string_conv *sc)
{
+ if (sc == NULL) {
+ return "current locale";
+ }
if (sc->flag & SCONV_TO_CHARSET)
return (sc->to_charset);
else
@@ -4123,7 +4126,12 @@ archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
* character-set. */
if ((aes->aes_set & AES_SET_MBS) == 0) {
const char *pm; /* unused */
- archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+ if (archive_mstring_get_mbs(a, aes, &pm) != 0) {
+ /* We have another form, but failed to convert it to
+ * the native locale. Transitively, we've failed to
+ * convert it to the specified character set. */
+ ret = -1;
+ }
}
/* If we already have an MBS form, use it to be translated to
* specified character-set. */
@@ -4141,6 +4149,8 @@ archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
if (length != NULL)
*length = aes->aes_mbs_in_locale.length;
} else {
+ /* Either we have no string in any form,
+ * or conversion failed and set 'ret != 0'. */
*p = NULL;
if (length != NULL)
*length = 0;
diff --git a/contrib/libarchive/libarchive/archive_write.c b/contrib/libarchive/libarchive/archive_write.c
index e1ce5d57288d..14dc7339ec5a 100644
--- a/contrib/libarchive/libarchive/archive_write.c
+++ b/contrib/libarchive/libarchive/archive_write.c
@@ -742,7 +742,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
if (a->format_write_header == NULL) {
archive_set_error(&(a->archive), -1,
- "Format must be set before you can write to an archive.");
+ "Format must be set before you can write to an archive");
a->archive.state = ARCHIVE_STATE_FATAL;
return (ARCHIVE_FATAL);
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c b/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c
index dbedf9d305c3..d2c4742d2ef6 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@@ -116,12 +119,20 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key
struct private_b64encode *state = (struct private_b64encode *)f->data;
if (strcmp(key, "mode") == 0) {
+ int64_t val;
+
if (value == NULL) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"mode option requires octal digits");
return (ARCHIVE_FAILED);
}
- state->mode = (int)atol8(value, strlen(value)) & 0777;
+ val = atol8(value, strlen(value));
+ if (val < 0 || val > INT_MAX) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "invalid mode option");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)val & 0777;
return (ARCHIVE_OK);
} else if (strcmp(key, "name") == 0) {
if (value == NULL) {
@@ -286,14 +297,19 @@ atol8(const char *p, size_t char_cnt)
{
int64_t l;
int digit;
-
+
+ if (char_cnt == 0)
+ return (-1);
+
l = 0;
while (char_cnt-- > 0) {
if (*p >= '0' && *p <= '7')
digit = *p - '0';
else
- break;
+ return (-1);
p++;
+ if (l > (INT64_MAX >> 3))
+ return (-1);
l <<= 3;
l |= digit;
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c b/contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c
index 2434528d5133..94b342d41b74 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c
@@ -127,8 +127,11 @@ archive_compressor_bzip2_options(struct archive_write_filter *f,
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
data->compression_level = value[0] - '0';
/* Make '0' be a synonym for '1'. */
/* This way, bzip2 compressor supports the same 0..9
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_gzip.c b/contrib/libarchive/libarchive/archive_write_add_filter_gzip.c
index b09e669b753d..0a2f22408bad 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_gzip.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_gzip.c
@@ -160,8 +160,11 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
data->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
@@ -172,8 +175,11 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
if (strcmp(key, "original-filename") == 0) {
free((void*)data->original_filename);
data->original_filename = NULL;
- if (value)
+ if (value) {
data->original_filename = strdup(value);
+ if (data->original_filename == NULL)
+ return (ARCHIVE_WARN);
+ }
return (ARCHIVE_OK);
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_lrzip.c b/contrib/libarchive/libarchive/archive_write_add_filter_lrzip.c
index fe974c93d5d0..cda34dc738d3 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_lrzip.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_lrzip.c
@@ -97,8 +97,11 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
struct write_lrzip *data = (struct write_lrzip *)f->data;
if (strcmp(key, "compression") == 0) {
- if (value == NULL)
- return (ARCHIVE_WARN);
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression option requires an argument");
+ return (ARCHIVE_FAILED);
+ }
else if (strcmp(value, "bzip2") == 0)
data->compression = bzip2;
else if (strcmp(value, "gzip") == 0)
@@ -109,13 +112,19 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
data->compression = none;
else if (strcmp(value, "zpaq") == 0)
data->compression = zpaq;
- else
- return (ARCHIVE_WARN);
+ else {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression invalid");
+ return (ARCHIVE_FAILED);
+ }
return (ARCHIVE_OK);
} else if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
data->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c b/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c
index 24061a169521..efc408e2a3f8 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c
@@ -160,8 +160,11 @@ archive_filter_lz4_options(struct archive_write_filter *f,
if (strcmp(key, "compression-level") == 0) {
int val;
if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
#ifndef HAVE_LZ4HC_H
if(val >= 3)
@@ -184,8 +187,11 @@ archive_filter_lz4_options(struct archive_write_filter *f,
}
if (strcmp(key, "block-size") == 0) {
if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "block-size invalid");
+ return (ARCHIVE_FAILED);
+ }
data->block_maximum_size = value[0] - '0';
return (ARCHIVE_OK);
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_lzop.c b/contrib/libarchive/libarchive/archive_write_add_filter_lzop.c
index 8580e58844af..42d62db63a13 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_lzop.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_lzop.c
@@ -211,8 +211,11 @@ archive_write_lzop_options(struct archive_write_filter *f, const char *key,
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
data->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c b/contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c
index 99c7a2cb7a7e..d25236fa9b26 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@@ -105,12 +108,20 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
struct private_uuencode *state = (struct private_uuencode *)f->data;
if (strcmp(key, "mode") == 0) {
+ int64_t val;
+
if (value == NULL) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"mode option requires octal digits");
return (ARCHIVE_FAILED);
}
- state->mode = (int)atol8(value, strlen(value)) & 0777;
+ val = atol8(value, strlen(value));
+ if (val < 0 || val > INT_MAX) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "invalid mode option");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)val & 0777;
return (ARCHIVE_OK);
} else if (strcmp(key, "name") == 0) {
if (value == NULL) {
@@ -277,14 +288,19 @@ atol8(const char *p, size_t char_cnt)
{
int64_t l;
int digit;
-
+
+ if (char_cnt == 0)
+ return (-1);
+
l = 0;
while (char_cnt-- > 0) {
if (*p >= '0' && *p <= '7')
digit = *p - '0';
else
- break;
+ return (-1);
p++;
+ if (l > (INT64_MAX >> 3))
+ return (-1);
l <<= 3;
l |= digit;
}
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_xz.c b/contrib/libarchive/libarchive/archive_write_add_filter_xz.c
index 098f0c95570b..56cf02070754 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_xz.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_xz.c
@@ -29,6 +29,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@@ -376,23 +379,33 @@ archive_compressor_xz_options(struct archive_write_filter *f,
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
- value[1] != '\0')
- return (ARCHIVE_WARN);
+ value[1] != '\0') {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
+ }
data->compression_level = value[0] - '0';
if (data->compression_level > 9)
data->compression_level = 9;
return (ARCHIVE_OK);
} else if (strcmp(key, "threads") == 0) {
char *endptr;
+ unsigned long val;
- if (value == NULL)
- return (ARCHIVE_WARN);
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "threads option requires an argument");
+ return (ARCHIVE_FAILED);
+ }
errno = 0;
- data->threads = (int)strtoul(value, &endptr, 10);
- if (errno != 0 || *endptr != '\0') {
+ val = strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) {
data->threads = 1;
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "threads invalid");
+ return (ARCHIVE_FAILED);
}
+ data->threads = (int)val;
if (data->threads == 0) {
#ifdef HAVE_LZMA_STREAM_ENCODER_MT
data->threads = lzma_cputhreads();
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
index d4752c247157..7149abb2b8a8 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
@@ -245,7 +245,9 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
if (strcmp(key, "compression-level") == 0) {
intmax_t level;
if (string_to_number(value, &level) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level invalid");
+ return (ARCHIVE_FAILED);
}
/* If we don't have the library, hard-code the max level */
int minimum = CLEVEL_MIN;
@@ -263,14 +265,18 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
}
#endif
if (level < minimum || level > maximum) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "compression-level out of range");
+ return (ARCHIVE_FAILED);
}
data->compression_level = (int)level;
return (ARCHIVE_OK);
} else if (strcmp(key, "threads") == 0) {
intmax_t threads;
if (string_to_number(value, &threads) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "threads invalid");
+ return (ARCHIVE_FAILED);
}
#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
@@ -286,7 +292,9 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
}
#endif
if (threads < 0 || threads > INT_MAX) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "threads out of rnage");
+ return (ARCHIVE_FAILED);
}
data->threads = (int)threads;
return (ARCHIVE_OK);
@@ -296,26 +304,34 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
return (ARCHIVE_OK);
} else if (strcmp(key, "min-frame-in") == 0) {
if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "min-frame-in invalid");
+ return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
} else if (strcmp(key, "min-frame-out") == 0 ||
strcmp(key, "min-frame-size") == 0) {
if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "min-frame-out invalid");
+ return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
} else if (strcmp(key, "max-frame-in") == 0 ||
strcmp(key, "max-frame-size") == 0) {
if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK ||
data->max_frame_in < 1024) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "max-frame-size invalid");
+ return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
} else if (strcmp(key, "max-frame-out") == 0) {
if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK ||
data->max_frame_out < 1024) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "max-frame-out invalid");
+ return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
#endif
@@ -323,22 +339,30 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
else if (strcmp(key, "long") == 0) {
intmax_t long_distance;
if (string_to_number(value, &long_distance) != ARCHIVE_OK) {
- return (ARCHIVE_WARN);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "long invalid");
+ return (ARCHIVE_FAILED);
}
#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream && ZSTD_VERSION_NUMBER >= MINVER_LONG
ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog);
if (ZSTD_isError(bounds.error)) {
int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
- if (((int)long_distance) < 10 || (int)long_distance > max_distance)
- return (ARCHIVE_WARN);
+ if (((int)long_distance) < 10 || (int)long_distance > max_distance) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "long out of range");
+ return (ARCHIVE_FAILED);
+ }
} else {
- if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound)
- return (ARCHIVE_WARN);
+ if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "long out of range");
+ return (ARCHIVE_FAILED);
+ }
}
#else
int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
if (((int)long_distance) < 10 || (int)long_distance > max_distance)
- return (ARCHIVE_WARN);
+ return (ARCHIVE_FAILED);
#endif
data->long_distance = (int)long_distance;
return (ARCHIVE_OK);
diff --git a/contrib/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libarchive/libarchive/archive_write_disk_posix.c
index cc59e58e4df6..5ee36888ef48 100644
--- a/contrib/libarchive/libarchive/archive_write_disk_posix.c
+++ b/contrib/libarchive/libarchive/archive_write_disk_posix.c
@@ -1975,7 +1975,7 @@ archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
return (a->lookup_gid)(a->lookup_gid_data, name, id);
return (id);
}
-
+
int64_t
archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
{
@@ -2119,7 +2119,7 @@ restore_entry(struct archive_write_disk *a)
if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
archive_set_error(&a->archive, en,
- "Hard-link target '%s' does not exist.",
+ "Hard-link target '%s' does not exist",
archive_entry_hardlink(a->entry));
return (ARCHIVE_FAILED);
}
@@ -2406,7 +2406,7 @@ create_filesystem_object(struct archive_write_disk *a)
*/
mode = final_mode & 0777 & ~a->user_umask;
- /*
+ /*
* Always create writable such that [f]setxattr() works if we're not
* root.
*/
@@ -3024,7 +3024,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
/*
* We are not the last element and we want to
* follow symlinks if they are a directory.
- *
+ *
* This is needed to extract hardlinks over
* symlinks.
*/
@@ -3435,7 +3435,7 @@ create_dir(struct archive_write_disk *a, char *path)
le = new_fixup(a, path);
if (le == NULL)
return (ARCHIVE_FATAL);
- le->fixup |=TODO_MODE_BASE;
+ le->fixup |= TODO_MODE_BASE;
le->mode = mode_final;
}
return (ARCHIVE_OK);
@@ -3447,8 +3447,17 @@ create_dir(struct archive_write_disk *a, char *path)
* don't add it to the fixup list here, as it's already been
* added.
*/
- if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
- return (ARCHIVE_OK);
+ if (errno == EEXIST) {
+ if (la_stat(path, &st) == 0) {
+ if (S_ISDIR(st.st_mode))
+ return (ARCHIVE_OK);
+ /* path exists but is not a directory */
+ errno = ENOTDIR;
+ } else {
+ /* restore original errno */
+ errno = EEXIST;
+ }
+ }
archive_set_error(&a->archive, errno, "Failed to create dir '%s'",
path);
@@ -4010,7 +4019,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
#elif defined(HAVE_CHFLAGS)
if (S_ISLNK(a->st.st_mode)) {
archive_set_error(&a->archive, errno,
- "Can't set file flags on symlink.");
+ "Can't set file flags on symlink");
return (ARCHIVE_WARN);
}
if (chflags(name, a->st.st_flags) == 0)
@@ -4569,7 +4578,7 @@ set_xattrs(struct archive_write_disk *a)
} else
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Cannot restore extended "
- "attributes on this file system.");
+ "attributes on this file system");
}
archive_string_free(&errlist);
@@ -4671,7 +4680,7 @@ set_xattrs(struct archive_write_disk *a)
} else
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Cannot restore extended "
- "attributes on this file system.");
+ "attributes on this file system");
}
archive_string_free(&errlist);
@@ -4767,4 +4776,3 @@ static void close_file_descriptor(struct archive_write_disk* a)
#endif /* !_WIN32 || __CYGWIN__ */
-
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_7zip.c b/contrib/libarchive/libarchive/archive_write_set_format_7zip.c
index 175285da13be..92ad810635ab 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_7zip.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_7zip.c
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@@ -333,6 +336,7 @@ string_to_number(const char *string, intmax_t *numberp)
if (string == NULL || *string == '\0')
return (ARCHIVE_WARN);
+ errno = 0;
*numberp = strtoimax(string, &end, 10);
if (end == string || *end != '\0' || errno == EOVERFLOW) {
*numberp = 0;
@@ -487,8 +491,9 @@ _7z_options(struct archive_write *a, const char *key, const char *value)
}
char *end = NULL;
+ errno = 0;
long lvl = strtol(value, &end, 10);
- if (end == NULL || *end != '\0') {
+ if (errno != 0 || end == NULL || *end != '\0') {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"parsing compression-level option value failed `%s'", value);
return (ARCHIVE_FAILED);
@@ -525,7 +530,7 @@ _7z_options(struct archive_write *a, const char *key, const char *value)
if (string_to_number(value, &threads) != ARCHIVE_OK) {
return (ARCHIVE_WARN);
}
- if (threads < 0) {
+ if (threads < 0 || threads > INT_MAX) {
return (ARCHIVE_WARN);
}
if (threads == 0) {
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_cpio_binary.c b/contrib/libarchive/libarchive/archive_write_set_format_cpio_binary.c
index aefb2ca6f514..4777e75f2912 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_cpio_binary.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_cpio_binary.c
@@ -506,12 +506,12 @@ write_header(struct archive_write *a, struct archive_entry *entry)
if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) &&
(archive_entry_size(entry) > 256*256*256-1)) {
archive_set_error(&a->archive, ERANGE,
- "File is too large for PWB binary cpio format.");
+ "File is too large for PWB binary cpio format");
ret_final = ARCHIVE_FAILED;
goto exit_write_header;
} else if (archive_entry_size(entry) > INT32_MAX) {
archive_set_error(&a->archive, ERANGE,
- "File is too large for binary cpio format.");
+ "File is too large for binary cpio format");
ret_final = ARCHIVE_FAILED;
goto exit_write_header;
}
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c b/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
index 254d5a9901c3..d72434d8ae88 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
@@ -322,7 +322,7 @@ write_header(struct archive_write *a, struct archive_entry *entry)
h + c_filesize_offset, c_filesize_size);
if (ret) {
archive_set_error(&a->archive, ERANGE,
- "File is too large for this format.");
+ "File is too large for this format");
ret_final = ARCHIVE_FAILED;
goto exit_write_header;
}
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_cpio_odc.c b/contrib/libarchive/libarchive/archive_write_set_format_cpio_odc.c
index c72c6b2796bf..ffac716b8e75 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_cpio_odc.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_cpio_odc.c
@@ -380,7 +380,7 @@ write_header(struct archive_write *a, struct archive_entry *entry)
h + c_filesize_offset, c_filesize_size);
if (ret) {
archive_set_error(&a->archive, ERANGE,
- "File is too large for cpio format.");
+ "File is too large for cpio format");
ret_final = ARCHIVE_FAILED;
goto exit_write_header;
}
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c b/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
index 14ef4cd2444d..b67007a631c0 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
@@ -293,6 +293,17 @@ archive_write_gnutar_header(struct archive_write *a,
} else
sconv = gnutar->opt_sconv;
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ && archive_entry_pathname_w(entry) == NULL
+#endif
+ ) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return ARCHIVE_FAILED;
+ }
+
/* Only regular files (not hardlinks) have data. */
if (archive_entry_hardlink(entry) != NULL ||
archive_entry_symlink(entry) != NULL ||
@@ -385,17 +396,30 @@ archive_write_gnutar_header(struct archive_write *a,
r = archive_entry_pathname_l(entry, &(gnutar->pathname),
&(gnutar->pathname_length), sconv);
if (r != 0) {
+ const char* p_mbs;
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for pathname");
ret = ARCHIVE_FATAL;
goto exit_write_header;
}
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Can't translate pathname '%s' to %s",
- archive_entry_pathname(entry),
- archive_string_conversion_charset_name(sconv));
- ret2 = ARCHIVE_WARN;
+ p_mbs = archive_entry_pathname(entry);
+ if (p_mbs) {
+ /* We have a wrongly-encoded MBS pathname.
+ * Warn and use it. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", p_mbs,
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ } else {
+ /* We have no MBS pathname. Fail. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname to %s",
+ archive_string_conversion_charset_name(sconv));
+ return ARCHIVE_FAILED;
+ }
}
r = archive_entry_uname_l(entry, &(gnutar->uname),
&(gnutar->uname_length), sconv);
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c b/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c
index c275c1ec92b9..359a2737365f 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_iso9660.c
@@ -1381,7 +1381,7 @@ iso9660_options(struct archive_write *a, const char *key, const char *value)
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"Option ``%s'' "
- "is not supported on this platform.", key);
+ "is not supported on this platform", key);
return (ARCHIVE_FATAL);
#endif
}
@@ -1503,7 +1503,7 @@ iso9660_options(struct archive_write *a, const char *key, const char *value)
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"``zisofs'' "
- "is not supported on this platform.");
+ "is not supported on this platform");
return (ARCHIVE_FATAL);
#endif
}
@@ -1539,7 +1539,7 @@ iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
if (archive_entry_filetype(entry) == AE_IFLNK
&& iso9660->opt.rr == OPT_RR_DISABLED) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Ignore symlink file.");
+ "Ignore symlink file");
iso9660->cur_file = NULL;
return (ARCHIVE_WARN);
}
@@ -1549,7 +1549,7 @@ iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"Ignore over %lld bytes file. "
- "This file too large.",
+ "This file too large",
MULTI_EXTENT_SIZE);
iso9660->cur_file = NULL;
return (ARCHIVE_WARN);
@@ -2103,7 +2103,7 @@ iso9660_close(struct archive_write *a)
if (iso9660->directories_too_deep != NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "%s: Directories too deep.",
+ "%s: Directories too deep",
archive_entry_pathname(
iso9660->directories_too_deep->file->entry));
return (ARCHIVE_WARN);
@@ -3799,7 +3799,7 @@ set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
isoent = isoent_find_entry(vdd->rootent, ids);
if (isoent == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Not Found %s `%s'.",
+ "Not Found %s `%s'",
label, ids);
return (ARCHIVE_FATAL);
}
@@ -7080,7 +7080,7 @@ isoent_make_path_table(struct archive_write *a)
* See also ISO9660 Standard 9.4.
*/
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Too many directories(%d) over 65535.", dir_number);
+ "Too many directories(%d) over 65535", dir_number);
return (ARCHIVE_FATAL);
}
@@ -7203,7 +7203,7 @@ isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)
else {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Boot image file(``%s'') size is too big "
- "for fd type.",
+ "for fd type",
iso9660->el_torito.boot_filename.s);
return (ARCHIVE_FATAL);
}
@@ -7964,7 +7964,7 @@ zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,
r = inflateInit(&zisofs->stream);
if (r != Z_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Can't initialize zisofs decompression.");
+ "Can't initialize zisofs decompression");
return (ARCHIVE_FATAL);
}
zisofs->stream_valid = 1;
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_mtree.c b/contrib/libarchive/libarchive/archive_write_set_format_mtree.c
index 8131574c8da2..a4ffec8954d9 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_mtree.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_mtree.c
@@ -2262,7 +2262,7 @@ mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Found duplicate entries for `%s' with "
- "differing file types.",
+ "differing file types",
np->pathname.s);
return (ARCHIVE_FAILED);
}
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libarchive/libarchive/archive_write_set_format_pax.c
index 66e6d75196ec..f3589d6859e3 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_pax.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_pax.c
@@ -1062,7 +1062,7 @@ archive_write_pax_header(struct archive_write *a,
}
/* If numeric GID is too large, add 'gid' to pax extended attrs. */
- if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) {
+ if (archive_entry_gid(entry_main) >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "gid",
archive_entry_gid(entry_main));
need_extension = 1;
@@ -1078,7 +1078,7 @@ archive_write_pax_header(struct archive_write *a,
}
/* If numeric UID is too large, add 'uid' to pax extended attrs. */
- if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) {
+ if (archive_entry_uid(entry_main) >= (1 << 18)) {
add_pax_attr_int(&(pax->pax_header), "uid",
archive_entry_uid(entry_main));
need_extension = 1;
@@ -1471,7 +1471,7 @@ archive_write_pax_header(struct archive_write *a,
if (r < ARCHIVE_WARN) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"archive_write_pax_header: "
- "'x' header failed?! This can't happen.\n");
+ "'x' header failed?! This can't happen");
archive_entry_free(entry_main);
archive_string_free(&entry_name);
return (ARCHIVE_FATAL);
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_ustar.c b/contrib/libarchive/libarchive/archive_write_set_format_ustar.c
index 4084eb455968..97724c1588b3 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_ustar.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_ustar.c
@@ -254,11 +254,11 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
sconv = ustar->opt_sconv;
/* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL
#if defined(_WIN32) && !defined(__CYGWIN__)
- if (archive_entry_pathname_w(entry) == NULL) {
-#else
- if (archive_entry_pathname(entry) == NULL) {
+ && archive_entry_pathname_w(entry) == NULL
#endif
+ ) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in tar file without pathname");
return (ARCHIVE_FAILED);
@@ -409,15 +409,28 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512],
*/
r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
if (r != 0) {
+ const char* p_mbs;
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
return (ARCHIVE_FATAL);
}
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Can't translate pathname '%s' to %s",
- pp, archive_string_conversion_charset_name(sconv));
- ret = ARCHIVE_WARN;
+ p_mbs = archive_entry_pathname(entry);
+ if (p_mbs) {
+ /* We have a wrongly-encoded MBS pathname.
+ * Warn and use it. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", p_mbs,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ } else {
+ /* We have no MBS pathname. Fail. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname to %s",
+ archive_string_conversion_charset_name(sconv));
+ return ARCHIVE_FAILED;
+ }
}
if (copy_length <= USTAR_name_size)
memcpy(h + USTAR_name_offset, pp, copy_length);
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c b/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c
index 2598fc076c0c..37ba73a1320a 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c
@@ -232,7 +232,11 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
sconv = v7tar->opt_sconv;
/* Sanity check. */
- if (archive_entry_pathname(entry) == NULL) {
+ if (archive_entry_pathname(entry) == NULL
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ && archive_entry_pathname_w(entry) == NULL
+#endif
+ ) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Can't record entry in tar file without pathname");
return (ARCHIVE_FAILED);
@@ -382,15 +386,28 @@ format_header_v7tar(struct archive_write *a, char h[512],
*/
r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
if (r != 0) {
+ const char* p_mbs;
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
return (ARCHIVE_FATAL);
}
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Can't translate pathname '%s' to %s",
- pp, archive_string_conversion_charset_name(sconv));
- ret = ARCHIVE_WARN;
+ p_mbs = archive_entry_pathname(entry);
+ if (p_mbs) {
+ /* We have a wrongly-encoded MBS pathname.
+ * Warn and use it. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", p_mbs,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ } else {
+ /* We have no MBS pathname. Fail. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname to %s",
+ archive_string_conversion_charset_name(sconv));
+ return ARCHIVE_FAILED;
+ }
}
if (strict && copy_length < V7TAR_name_size)
memcpy(h + V7TAR_name_offset, pp, copy_length);
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_xar.c b/contrib/libarchive/libarchive/archive_write_set_format_xar.c
index ec3219057e4e..6ecccdef6499 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_xar.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_xar.c
@@ -526,12 +526,13 @@ xar_options(struct archive_write *a, const char *key, const char *value)
}
if (strcmp(key, "threads") == 0) {
char *endptr;
+ unsigned long val;
if (value == NULL)
return (ARCHIVE_FAILED);
errno = 0;
- xar->opt_threads = (int)strtoul(value, &endptr, 10);
- if (errno != 0 || *endptr != '\0') {
+ val = strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) {
xar->opt_threads = 1;
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
@@ -539,6 +540,7 @@ xar_options(struct archive_write *a, const char *key, const char *value)
value);
return (ARCHIVE_FAILED);
}
+ xar->opt_threads = (int)val;
if (xar->opt_threads == 0) {
#ifdef HAVE_LZMA_STREAM_ENCODER_MT
xar->opt_threads = lzma_cputhreads();
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_zip.c b/contrib/libarchive/libarchive/archive_write_set_format_zip.c
index 19121b519148..81d3db0db038 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_zip.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_zip.c
@@ -398,16 +398,17 @@ archive_write_zip_options(struct archive_write *a, const char *key,
return (ret);
} else if (strcmp(key, "compression-level") == 0) {
char *endptr;
+ unsigned long v;
if (val == NULL)
return (ARCHIVE_WARN);
errno = 0;
- zip->compression_level = (short)strtoul(val, &endptr, 10);
- if (errno != 0 || *endptr != '\0' || zip->compression_level < 0 ||
- zip->compression_level > 9) {
+ v = strtoul(val, &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || v > 9) {
zip->compression_level = 6; // set to default
return (ARCHIVE_WARN);
}
+ zip->compression_level = (short)v;
if (zip->compression_level == 0) {
zip->requested_compression = COMPRESSION_STORE;
@@ -435,17 +436,19 @@ archive_write_zip_options(struct archive_write *a, const char *key,
}
} else if (strcmp(key, "threads") == 0) {
char *endptr;
+ unsigned long v;
if (val == NULL)
return (ARCHIVE_FAILED);
errno = 0;
- zip->threads = (short)strtoul(val, &endptr, 10);
- if (errno != 0 || *endptr != '\0') {
+ v = strtoul(val, &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || v > SHRT_MAX) {
zip->threads = 1;
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"Illegal value `%s'", val);
return (ARCHIVE_FAILED);
}
+ zip->threads = (short)v;
if (zip->threads == 0) {
#ifdef HAVE_LZMA_STREAM_ENCODER_MT
zip->threads = lzma_cputhreads();
@@ -802,6 +805,17 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
int version_needed = 10;
#define MIN_VERSION_NEEDED(x) do { if (version_needed < x) { version_needed = x; } } while (0)
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ && archive_entry_pathname_w(entry) == NULL
+#endif
+ ) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in zip file without pathname");
+ return ARCHIVE_FAILED;
+ }
+
/* Ignore types of entries that we don't support. */
type = archive_entry_filetype(entry);
if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
@@ -882,22 +896,33 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_FATAL);
}
- if (sconv != NULL) {
+ {
const char *p;
size_t len;
if (archive_entry_pathname_l(zip->entry, &p, &len, sconv) != 0) {
+ const char* p_mbs;
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
return (ARCHIVE_FATAL);
}
- archive_set_error(&a->archive,
- ARCHIVE_ERRNO_FILE_FORMAT,
- "Can't translate Pathname '%s' to %s",
- archive_entry_pathname(zip->entry),
- archive_string_conversion_charset_name(sconv));
- ret2 = ARCHIVE_WARN;
+ p_mbs = archive_entry_pathname(zip->entry);
+ if (p_mbs) {
+ /* We have a wrongly-encoded MBS pathname. Warn and use it. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", p_mbs,
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ } else {
+ /* We have no MBS pathname. Fail. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname to %s",
+ archive_string_conversion_charset_name(sconv));
+ return ARCHIVE_FAILED;
+ }
}
if (len > 0)
archive_entry_set_pathname(zip->entry, p);
diff --git a/contrib/libarchive/libarchive/test/test_acl_text.c b/contrib/libarchive/libarchive/test/test_acl_text.c
index c2904649a3ba..7e80686e84eb 100644
--- a/contrib/libarchive/libarchive/test/test_acl_text.c
+++ b/contrib/libarchive/libarchive/test/test_acl_text.c
@@ -404,6 +404,29 @@ DEFINE_TEST(test_acl_from_text)
archive_entry_acl_clear(ae);
free(ws);
+
+ /*
+ * 6. Malformed "default" prefix with no tag field should return
+ * ARCHIVE_WARN, not crash (GitHub issue #2744).
+ * When the ACL text is just "d" or "default" with type DEFAULT,
+ * the parser recognises the default prefix but field[1] is NULL,
+ * which previously caused a NULL-pointer dereference.
+ */
+ archive_entry_acl_clear(ae);
+ assertEqualInt(ARCHIVE_WARN,
+ archive_entry_acl_from_text(ae, "d",
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+ assertEqualInt(ARCHIVE_WARN,
+ archive_entry_acl_from_text_w(ae, L"d",
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+ archive_entry_acl_clear(ae);
+ assertEqualInt(ARCHIVE_WARN,
+ archive_entry_acl_from_text(ae, "default",
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+ assertEqualInt(ARCHIVE_WARN,
+ archive_entry_acl_from_text_w(ae, L"default",
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT));
+
archive_entry_free(ae);
}
diff --git a/contrib/libarchive/libarchive/test/test_archive_pathmatch.c b/contrib/libarchive/libarchive/test/test_archive_pathmatch.c
index 3696d38fcf19..3b212aca9d94 100644
--- a/contrib/libarchive/libarchive/test/test_archive_pathmatch.c
+++ b/contrib/libarchive/libarchive/test/test_archive_pathmatch.c
@@ -285,4 +285,22 @@ DEFINE_TEST(test_archive_pathmatch)
archive_pathmatch("a/b/c$", "a/b/c/d", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
assertEqualInt(1,
archive_pathmatch("b/c/d$", "a/b/c/d", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+
+ /* Anchor characters within pattern not special. */
+ assertEqualInt(0,
+ archive_pathmatch("*^*", "a/b/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(1,
+ archive_pathmatch("*^*", "a^b", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(0,
+ archive_pathmatch("*$*", "a/b/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(1,
+ archive_pathmatch("*$*", "a$b", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(0,
+ archive_pathmatch("a*/^b/c", "a/b/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(1,
+ archive_pathmatch("a*/^b/c", "a/^b/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(0,
+ archive_pathmatch("a*/b$/c", "a/b/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
+ assertEqualInt(1,
+ archive_pathmatch("a*/b$/c", "a/b$/c", PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END));
}
diff --git a/contrib/libarchive/libarchive/test/test_archive_string_conversion.c b/contrib/libarchive/libarchive/test/test_archive_string_conversion.c
index 055bddc75068..ecc39dffea7f 100644
--- a/contrib/libarchive/libarchive/test/test_archive_string_conversion.c
+++ b/contrib/libarchive/libarchive/test/test_archive_string_conversion.c
@@ -883,6 +883,141 @@ DEFINE_TEST(test_archive_string_conversion)
test_archive_string_set_get();
}
+static void
+test_archive_string_conversion_fail_charset(void)
+{
+ /* Conversion error message construction may use the charset name. */
+ assertEqualString("current locale",
+ archive_string_conversion_charset_name(NULL));
+}
+
+static void
+test_archive_string_conversion_fail_utf16_mbs(struct archive *a,
+ struct archive_string_conv *sconv)
+{
+ static const wchar_t wcs_string[] = L"\U0000043f\U00000440\U00000438";
+ int r;
+ const char* p;
+ size_t len;
+
+ /* WCS to MBS should fail. */
+ {
+ struct archive_string str;
+ archive_string_init(&str);
+ r = archive_string_append_from_wcs(
+ &str, wcs_string, sizeof(wcs_string) / sizeof(*wcs_string) - 1);
+ assertEqualInt(-1, r);
+ archive_string_free(&str);
+ }
+ {
+ struct archive_mstring mstr;
+ memset(&mstr, 0, sizeof(mstr));
+ assertEqualInt(ARCHIVE_OK,
+ archive_mstring_copy_wcs(&mstr, wcs_string));
+ r = archive_mstring_get_mbs_l(a, &mstr, &p, &len, NULL);
+ assertEqualInt(-1, r);
+ assertEqualInt(0, mstr.aes_set & AES_SET_MBS);
+ archive_mstring_clean(&mstr);
+ }
+ if (sconv) {
+ struct archive_mstring mstr;
+ memset(&mstr, 0, sizeof(mstr));
+ assertEqualInt(ARCHIVE_OK,
+ archive_mstring_copy_wcs(&mstr, wcs_string));
+ r = archive_mstring_get_mbs_l(a, &mstr, &p, &len, sconv);
+ assertEqualInt(-1, r);
+ assertEqualInt(0, mstr.aes_set & AES_SET_MBS);
+ archive_mstring_clean(&mstr);
+ }
+}
+
+static void
+test_archive_string_conversion_fail_utf8_mbs(struct archive *a,
+ struct archive_string_conv *sconv)
+{
+ static const char utf8_string[] = "\xD0\xBF\xD1\x80\xD0\xB8";
+ int r;
+ const char* p;
+ size_t len;
+
+ /* UTF-8 to MBS should fail. */
+ {
+ struct archive_mstring mstr;
+ memset(&mstr, 0, sizeof(mstr));
+ assertEqualInt(6,
+ archive_mstring_copy_utf8(&mstr, utf8_string));
+ r = archive_mstring_get_mbs_l(a, &mstr, &p, &len, NULL);
+ assertEqualInt(-1, r);
+ assertEqualInt(0, mstr.aes_set & AES_SET_MBS);
+ archive_mstring_clean(&mstr);
+ }
+ if (sconv) {
+ struct archive_mstring mstr;
+ memset(&mstr, 0, sizeof(mstr));
+ assertEqualInt(6,
+ archive_mstring_copy_utf8(&mstr, utf8_string));
+ r = archive_mstring_get_mbs_l(a, &mstr, &p, &len, sconv);
+ assertEqualInt(-1, r);
+ assertEqualInt(0, mstr.aes_set & AES_SET_MBS);
+ archive_mstring_clean(&mstr);
+ }
+}
+
+DEFINE_TEST(test_archive_string_conversion_fail_c)
+{
+ struct archive *a;
+
+ /* Test the C locale by not calling setlocale. */
+
+ test_archive_string_conversion_fail_charset();
+
+ assert((a = archive_write_new()) != NULL);
+
+ test_archive_string_conversion_fail_utf16_mbs(a, NULL);
+ test_archive_string_conversion_fail_utf8_mbs(a, NULL);
+
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+DEFINE_TEST(test_archive_string_conversion_fail_latin1)
+{
+ struct archive *a;
+ struct archive_string_conv *sconv;
+
+ /* Test a Latin-1 locale. */
+ if (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Windows allows ".<code-page>" to change encoding. */
+ setlocale(LC_ALL, ".1252") == NULL
+#else
+ setlocale(LC_ALL, "en_US.ISO8859-1") == NULL
+#endif
+ ) {
+ skipping("No Latin-1 locale found on this system.");
+ return;
+ }
+
+ test_archive_string_conversion_fail_charset();
+
+ assert((a = archive_write_new()) != NULL);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ assertA(NULL != (sconv =
+ archive_string_conversion_to_charset(a, "CP1252", 0)));
+ assertEqualString("CP1252",
+ archive_string_conversion_charset_name(sconv));
+#else
+ assertA(NULL != (sconv =
+ archive_string_conversion_to_charset(a, "ISO8859-1", 0)));
+ assertEqualString("ISO8859-1",
+ archive_string_conversion_charset_name(sconv));
+#endif
+ test_archive_string_conversion_fail_utf16_mbs(a, sconv);
+ test_archive_string_conversion_fail_utf8_mbs(a, sconv);
+
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
DEFINE_TEST(test_archive_string_conversion_utf16_utf8)
{
#if !defined(_WIN32) || defined(__CYGWIN__)
diff --git a/contrib/libarchive/libarchive/test/test_gnutar_filename_encoding.c b/contrib/libarchive/libarchive/test/test_gnutar_filename_encoding.c
index 476ec2149fdf..830b3884f829 100644
--- a/contrib/libarchive/libarchive/test/test_gnutar_filename_encoding.c
+++ b/contrib/libarchive/libarchive/test/test_gnutar_filename_encoding.c
@@ -491,3 +491,43 @@ DEFINE_TEST(test_gnutar_filename_encoding_UTF16_win)
assertEqualMem(buff + 157, "\xE8\xA1\xA8.txt", 7);
#endif
}
+
+DEFINE_TEST(test_gnutar_filename_encoding_fail_UTF16_win)
+{
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ skipping("This test is meant to verify unicode string handling"
+ " on Windows with UTF-16 names");
+ return;
+#else
+ struct archive *a;
+ struct archive_entry *entry;
+ char buff[4096];
+ size_t used;
+
+ /* Test the C locale by not calling setlocale. */
+
+ a = archive_write_new();
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_gnutar(a));
+ if (archive_write_set_options(a, "hdrcharset=CP437") != ARCHIVE_OK) {
+ skipping("This system cannot convert character-set"
+ " from UTF-16 to CP437.");
+ archive_write_free(a);
+ return;
+ }
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ entry = archive_entry_new2(a);
+ /* Set the filename using a UTF-16 string */
+ archive_entry_copy_pathname_w(entry, L"\u8868.txt");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, entry));
+ /* The pathname cannot even be represented in the current locale
+ for inclusion in the error message. */
+ assertEqualString("Can't translate pathname to CP437",
+ archive_error_string(a));
+ archive_entry_free(entry);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+#endif
+}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip.c b/contrib/libarchive/libarchive/test/test_read_format_7zip.c
index 3236fee2c9d3..6d71f80d12ed 100644
--- a/contrib/libarchive/libarchive/test/test_read_format_7zip.c
+++ b/contrib/libarchive/libarchive/test/test_read_format_7zip.c
@@ -1383,6 +1383,32 @@ DEFINE_TEST(test_read_format_7zip_sfx_elf)
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+/*
+ * A truncated ELF 64-bit MSB file.
+ */
+DEFINE_TEST(test_read_format_7zip_sfx_elf64trunc)
+{
+ const char *reffile = "test_read_format_7zip_sfx_elf64trunc.elf";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assert((a = archive_read_new()) != NULL);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+
+ extract_reference_file(reffile);
+ assertEqualIntA(a, ARCHIVE_FATAL,
+ archive_read_open_filename(a, reffile, 10240));
+
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+ assertEqualInt(0, archive_file_count(a));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_read_format_7zip_extract_second)
{
struct archive *a;
diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed.c b/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed.c
index f2120879fa8c..bd3452c00aad 100644
--- a/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed.c
+++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed.c
@@ -59,8 +59,25 @@ test_malformed2(void)
assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
}
+
+static void
+test_malformed3(void)
+{
+ const char *refname = "test_read_format_7zip_malformed3.7z";
+ struct archive *a;
+
+ extract_reference_file(refname);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open_filename(a, refname, 10240));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_read_format_7zip_malformed)
{
test_malformed1();
test_malformed2();
+ test_malformed3();
}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed3.7z.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed3.7z.uu
new file mode 100644
index 000000000000..4e814980a6da
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_malformed3.7z.uu
@@ -0,0 +1,24 @@
+begin 644 test_read_format_7zip_malformed3.7z
+M?T5,1@("`64N9&5B=0``+ZZNRO_______P```/\Q```````````"````````
+M```L0"!S+F)S,``1<P!```H``FMK__](:VMK:VNAH:$!`*&AH:&A)S$```!C
+M;VUP>FEP503_8G-S90``````#0H-_P```'X```````````(`````````````
+M`*D``````"\`````____^@````$````````#`/__________<RYD96)U```O
+MKJ[*________````_S$```````````(``````````"Q`(',N8G,P`!%S`$``
+M(``":VNAH2<Q```````#Z'K__P5%145%____________`/__________;VUP
+M>FEP503_8G-S90``````#0H-_P```'X```````````(``````````````*D`
+M`````"\`````____^@````$````````#_P```/______<RYD96)U```OKJ[*
+M________````_S$```````````(``````````"Q`(',N8G,P`!%S`$"0`P`"
+M:VLR:TAK:VMK:Z&AH0$`H:&AH:$G,@```&-O;7!Z:7!5!/]28W-E```````-
+M"@W_````?@```````````@``````````````J0``````+P`````0___Z````
+M`0````````,`__________\PXT$N9&%T80#^E)3+E)24_P3_____________
+M____(________^+______P5%145%____________`/__________________
+M_____________________^?_________145%0``#`/Z4E,N4P<'!P<'!E```
+M`/_R`````'5U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U
+M=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=0`````"````
+M```````L0"!S+F4```````T*#?\```!^```````````"``````````````"I
+M```````O`````/___^0````!`````````P#__W5U=75U=75U=75U=75U=75U
+M=0`````"```````````L0"!S+F)S,?P1<P!```,`"&MK,FM(:VMK145%145%
+,P45%145%1?____\$
+`
+end
+
diff --git a/contrib/libarchive/libarchive/test/test_read_format_7zip_sfx_elf64trunc.elf.uu b/contrib/libarchive/libarchive/test/test_read_format_7zip_sfx_elf64trunc.elf.uu
new file mode 100644
index 000000000000..512da918218b
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_7zip_sfx_elf64trunc.elf.uu
@@ -0,0 +1,5 @@
+begin 664 test_read_format_7zip_sfx_elf64trunc.elf
+M?T5,1@("````````````````````````````````````````````````````
+2```````````````````H``$`
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.c b/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.c
new file mode 100644
index 000000000000..a4c15d62f789
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.c
@@ -0,0 +1,45 @@
+#include "test.h"
+
+/*
+ * Regression test for Heap Out-of-Bounds Write in CAB LZX decoder.
+ * This ensures that a malformed CFDATA uncompressed size does not
+ * bypass physical buffer limits and cause memory corruption during skips.
+ */
+DEFINE_TEST(test_read_format_cab_lzx_oob)
+{
+ const char *refname = "test_read_format_cab_lzx_oob.cab";
+ struct archive *a;
+ struct archive_entry *ae;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ /* * The test framework will automatically find 'test_read_format_cab_lzx_oob.cab.uu',
+ * decode it, and place the binary '.cab' in the temporary test directory.
+ */
+ extract_reference_file(refname);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+
+ /* If it fails to open, there's a problem with the test setup/file */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240));
+
+ /* Read the header of the malformed entry */
+ if (ARCHIVE_OK == archive_read_next_header(a, &ae)) {
+ /* * We attempt to read a block to initialize the LZX state machine.
+ * We do not assert the result because the file is intentionally malformed.
+ * Regardless of success or failure, we force a skip to test state handling
+ * and trigger the vulnerability.
+ */
+ archive_read_data_block(a, &buff, &size, &offset);
+ archive_read_data_skip(a);
+ /* * Optional: We could assert that the error string contains our patch message,
+ * but simply surviving without a segfault/ASAN violation is the primary goal
+ * for fuzzing regression tests.
+ */
+ }
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+} \ No newline at end of file
diff --git a/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.cab.uu b/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.cab.uu
new file mode 100644
index 000000000000..2f453ef27116
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_cab_lzx_oob.cab.uu
@@ -0,0 +1,11 @@
+begin 664 test_read_format_cab_lzx_oob.cab
+M35-#1@````!!``"2DI*2DI(````````2Y``!``$``,2PW@``'P$```$`0Q`!
+M`3O2T@D)"0D)"0D)``````````````"RLK*RLK*RLK*RLK*RLK*RLK(*,```
+M````````````````LK*2X____[:RL@``````````LK*R"C``````````````
+M`````+*RDN/___^VLK(```````````"R'YV)3``````````````0$!`0$!`0
+M$!`0$!D0$!`0$!`0$!`0$!`0$!`0$!#___\/$!`0`````````$`0$`!`,#<P
+M-S`R````,#82SWXO+R\`_P``````````````````````````````````````
+M``````#U\DH*-S`R````,#82SWXO+R\`_P``````````````````````````
+.````````````````````
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.c b/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.c
new file mode 100644
index 000000000000..05cc80b79c93
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.c
@@ -0,0 +1,41 @@
+#include "test.h"
+
+DEFINE_TEST(test_read_format_cab_skip_malformed)
+{
+ /* Reference to the malformed CAB file */
+ const char *refname = "test_read_format_cab_skip_malformed.cab";
+ struct archive *a;
+ struct archive_entry *ae;
+ void *buffer;
+ size_t buffersize;
+
+ /* Extract the reference file into the test sandbox */
+ extract_reference_file(refname);
+
+ /* Read the entire file into memory */
+ buffer = slurpfile(&buffersize, "%s", refname);
+ assert(buffer != NULL);
+
+ /* Initialize the archive reader */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+
+ /* Read from memory (a prerequisite for triggering this specific bug) */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buffer, buffersize));
+
+ /* Simulate the parsing flow to trigger the implicit skip routine */
+ while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
+ const void *buff;
+ size_t size_read;
+ int64_t offset;
+ while (archive_read_data_block(a, &buff, &size_read, &offset) == ARCHIVE_OK) {
+ /* Consume data. This will fail quickly due to the malformed payload. */
+ }
+ }
+
+ /* Clean up. If the patch is effective, the program reaches here safely. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ free(buffer);
+} \ No newline at end of file
diff --git a/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.cab.uu b/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.cab.uu
new file mode 100644
index 000000000000..7ec83433546d
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_cab_skip_malformed.cab.uu
@@ -0,0 +1,95 @@
+begin 664 test_read_format_cab_skip_malformed.cab
+M35-#1@````!!``"2DI*2DI(````````2Y``!``$``,2PW@``'P$``-(!```!
+M.]+2"0D)"0D)"0D)"0D)"0D)"0FRLK*R"0D)"0D)"0D)"0D)LK*RLK*RLK(`
+M``````````````````"RLK*RLK*RLK*RLK*RLK*RLK(*,```````````````
+M````LK*RX____[:RL@```````````````#`W,"]`````"G!P`````&UDL@HP
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````%!+`P0````!#`P,#`P,#`P,#`P,#```````````````__\```3_
+M``!(___H````````````````````````````````````````____________
+M____________________________________________________````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M@```````````````@`````$`````````````````````````````````````
+M````````````````````````````````````````````#`H,#````'H,#`P`
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````#__P``!/\``$C__^@```````````#_________________________
+M______________________________________\`````````````````````
+M`"\O+R\O+R\O+R\O+R\O+R\O+R\O+R\G+PHQ-C4P+U):22\P-S!,4EI)"@HO
+M=6YS970@<F$@("T@("!/3T]/3T]/3T]/3T]/3T]/3T]/3T]/3T\@(2`@("`@
+M+2`@("`@("`@(",@("`@("`@("L@("`@("`@("`@("`@("`@("`@("`@("`@
+M("`K('A,4EI)"@H*("`@("`@(```````````````````````````````````
+M```````````````````````````````````````````````````,]@P,#`H*
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````#__P``!/\``$C__^@`````
+M`%``````````````````````````````````________________________
+M________________________________________````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````@```````
+M````````@`````$`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````#__P``!/\``$C__^@`
+M``````````#_________________________________________________
+M______________\`````````````````````````````````````````````
+M`````````````````````%!+`P0````!#`P,#`P,#`P,#`P,#```#`H,#```
+M`'H,````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````,#`SV``````````````````````P,
+M#`H*`/__```$_P``2/__Z````````````%#_________________________
+M______________________________________\`````````````````````
+M````````````````````````````````````````````+R\P23@*"DQ:3#`P
+M"DQ26DD*"DQ:+PH*,#`W,"\O,#<P+R\O+R\O+S!).`H*3%I,,"\P-S!,4EI)
+M"@I,6B\O"F)E9VEN-F%E<RUB-"`Q(%TP-#`P5T%20R\O25HX"DQ,,`I,4EI)
+M"@I,6B\O-S`W"DQ:3#`O,#<P3%):20H*3%HO+PIB96=I;BUB87-E-C0@-B`P
+M-#`P5T%20R\O25HX"DQ,,`I,4EI)"@I,6B\O-S`W,#<O+R]:4B\O+R\O"C!@
+M-S`W,#$*#8V-C8V-C8V-C8V-C8V-C8V-C5!+`P10"@`*"C`P-S`O+S`W,"\O
+M+R\O+R\P23@*"DQ:3#`O,#<P3%):20H*3%HO+PIB96=I;BUB87-E-C0@,2`P
+M-#`P5T%20R\O25HX"DQ,"DQ:3#`O,#<P3%):20H*3%HO+PIB96=I;BUB87-E
+M-C0@,2`P-#`P5T%20R\O25HV"DQ,,`I,4EI)"@I,6B\*"C`P-S`O+S`W,"\O
+M+R\O+R\P23@*"DQ:3#`O,#<P3%):20H*3%HO+PIB90``V/\M`?\-__\-_RO_
+M_?____\F__\+____B?\*)PH``')R<G)R<G)R<G+_____________________
+M____________________________________________________________
+M____________________________________________________________
+M____________________________________________________<G)R<G)R
+M<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)R<G)``!0`
+M`````````````````$#_____"`1W2"__0`#D$P`G_D3________NXEQ<7%QR
+M7#`P-S=<,#)<7%Q<7%Q<?%Q<7%Q<<EPP,#<W7#`R7%Q<7%Q<7%Q<,#)<7%Q<
+M7%Q<7')<,#`W-UPP,EQ<7%Q<7"L*=75,6B\O"F)E9VEN+6)A<V4V-"`V(#`T
+M,#!705)#+R])6C@*3"`Q(#`T,#!705)#+R])6C@*3$P*3%I,,"\P-S!,4EI)
+M"@I,6B\O"F)E9VEN+6)A<V4V-"`Q(#`T,#!705)#+R])6C@*3$PP"DQ26DD*
+M"DQ:+PH*,#`W,"\O,#<P+R\O+R\O+S!).`H*3%I,,"\P-S!,4EI)"@I,6B\O
+M"F)E9VEN+6)A<V4V-"`Q,S`T,#!705)#+R])6C@*3$PP"DQ26DD*"DQ:+R\W
+M,#<*3%I,,"\P-S!,4EI)"@I,6B\O"F)E9VEN+6)A<V4V-"`V(#`T,#!705)#
+M+R])6C@*3$PP"DQ26DD*"DQ:+R\W,#<P-R\O+UI2+R\O+R\O+R\O-S`W"DQ:
+M3#`O04Y325\P-S!,4EI)"@I,6B\O"F)E9VEN+6)A<V4V-"`V(#`T,#!705)#
+M+R])6CP*3$PP"DQ26DD*"DQ:+R\W,#<P-R\O+UI2+R\O+R\O+R\O+R\O+R\O
+M+R\O+R\O+R\O+R\P23@*"DQ:3"\O+R\O+R\P23@*"DQ:3#`O,#<P3%):20H*
+M3%HO+PIB96=I;BUB87-E-C0@,2`P-#`P5T%20R\O25HX"DQ,,`I,4EI)"@I,
+M6B\O-S`W"DQ:3#`O,#<P3%):20H*3%HO+PIB96=I;BUB87-/-C0@-B`P-#`P
+M5T%20R\O25HX"DQ,,`I,4EI)"@I,6B\O-S`W,#<O+R]:4B\O+R\O+R\O+S<P
+M-PI,6DPP+S`W,$Q26DD*"DQ:+R\*8F5G:6XM8F%S938T(#8@,#0P,%=!4D,O
+M,#<P3%):20H*3%HO+PIB96=I;BUB87-E-C0@,2!=,#0P,%=!4D,O+TE:.`I,
+M3#`*3%):20H*3%HO+S<P-PI,6DPP+S`W,$Q26DD*"DQ:+R\*8F5G:6XM8F%S
+M938T(#8@,#0P,%=!4D,O+TE:.`I,3#`*3%):20H*,!^+#@``````````````
+M```````+``````#'<?\```````````L``````,=Q_____U):20```````!`D
+M````````3%HO+S<P-S`W+R\O6E(O+R]Z+R\*,&`W,#<P,0H-"@U=2@T*#0HN
+M+@H*#0!$`````%Q<7%QA7%Q<55Q<05PR*5Q<7%Q<7%Q<7%PP7%Q!7#(I7%Q<
+M7%Q<7%Q<7#!<7$%<,BE<7%Q<85Q<7%5<7$%<,BE<7%Q<7%Q<7%Q<,%Q<7%Q<
+M7%Q<7%Q57%Q!7#(I7`D)"0D)"0D)"0D)"0D)"0D)"0D)"@````!%1@"']@``
+!````
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.c b/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.c
new file mode 100644
index 000000000000..bad52b154acd
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) 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 "test.h"
+
+/*
+ * Verify that a crafted ISO9660 image with an invalid zisofs block-size
+ * exponent (pz_log2_bs) is handled gracefully.
+ *
+ * The ZF extension in the Rock Ridge entry stores pz_log2_bs as a raw
+ * byte from the image. The zisofs spec only permits values 15-17.
+ * Values outside that range can cause:
+ * - Undefined behavior via oversized bit shifts (any platform)
+ * - Integer overflow in block pointer allocation on 32-bit platforms,
+ * leading to a heap buffer overflow write
+ *
+ * The test image has pz_log2_bs=2 (out of spec) combined with
+ * pz_uncompressed_size=0xFFFFFFF9. On 32-bit, (ceil+1)*4 overflows
+ * size_t to 0, malloc(0) returns a tiny buffer, and the code attempts
+ * to write ~4GB into it. On 64-bit the allocation is huge and safely
+ * fails.
+ *
+ * We verify the fix by checking archive_entry_size() after reading the
+ * header. When pz_log2_bs validation rejects the bad value (pz=0),
+ * the entry keeps its raw on-disk size (small). Without the fix,
+ * the reader sets the entry size to pz_uncompressed_size (0xFFFFFFF9).
+ *
+ * We intentionally do NOT call archive_read_data() here. Without the
+ * fix, the data-read path triggers a heap buffer overflow on 32-bit
+ * that silently corrupts the process heap, causing later tests to
+ * crash rather than this one.
+ */
+DEFINE_TEST(test_read_format_iso_zisofs_overflow)
+{
+ const char reffile[] = "test_read_format_iso_zisofs_overflow.iso";
+ struct archive *a;
+ struct archive_entry *ae;
+ int r = ARCHIVE_OK;
+ int found_regular_file = 0;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, reffile, 10240));
+
+ while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK ||
+ r == ARCHIVE_WARN) {
+ /*
+ * With the fix, pz_log2_bs=2 is rejected and pz is set
+ * to 0, so the entry keeps its small raw size from the
+ * ISO directory record. Without the fix, zisofs sets
+ * the entry size to pz_uncompressed_size (0xFFFFFFF9).
+ *
+ * We intentionally do NOT call archive_read_data().
+ * Without the fix, the data-read path triggers a heap
+ * buffer overflow on 32-bit that silently corrupts the
+ * process heap, causing later tests to crash rather
+ * than this one.
+ */
+ if (archive_entry_filetype(ae) == AE_IFREG) {
+ la_int64_t sz = archive_entry_size(ae);
+ failure("entry \"%s\" has size %jd"
+ "; expected < 1 MiB"
+ " (if size is 4294966265 = 0xFFFFFFF9, the"
+ " pz_log2_bs validation is missing)",
+ archive_entry_pathname(ae), (intmax_t)sz);
+ assert(sz < 1024 * 1024);
+ found_regular_file = 1;
+ }
+ }
+
+ /* Iteration must have completed normally. */
+ assertEqualInt(ARCHIVE_EOF, r);
+
+ /* The PoC image contains a regular file; if we never saw one,
+ * something is wrong with the test image. */
+ assert(found_regular_file);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu b/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu
new file mode 100644
index 000000000000..5e7dcc3750ac
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu
@@ -0,0 +1,1096 @@
+begin 664 test_read_format_iso_zisofs_overflow.iso
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````!0T0P,#$!````````````````````````````````````````
+M````4$]#7U=2251%`````````````````````````````````````````!@`
+M```````8```````````````````````````````````````````!```!`0``
+M`0`("``*````````"A(`````````````$@`````B`!,````````3``@`````
+M"`!Z`1D,`````@```0```0$`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````$`````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````/]#1#`P,0$`````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````0`3`````0``````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!5`!,````````3``@`
+M````"`!Z`1D,`````@```0```0$`4U`'`;[O`%!8+`'M00````!![0(`````
+M```"``````````````````````$````````!(@`3````````$P`(``````@`
+M>@$9#`````(```$```$!`7T`%````````!00"``````($'H!&0P````````!
+M```!#D]615)&3$]7+D))3CLQ`%I&$`%P>@0"^?O__P````!.31$!`$]615)&
+M3$]7+D))3E!8+`&D@0````"!I`$````````!``````````````````````(`
+M```````"````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#?D4Y;)V]8'^?O__P0"``!!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!
+M0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"
+M0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#
+M1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$04)#1$%"0T1!0D-$
+M04)#1```````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+,````````````````
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.c b/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.c
new file mode 100644
index 000000000000..fbdbd671df7c
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2026 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) 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 "test.h"
+
+DEFINE_TEST(test_read_format_lha_oversize_header)
+{
+ const char *refname = "test_read_format_lha_oversize_header.lzh";
+ extract_reference_file(refname);
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240));
+
+ /* First 18 entries in the test file are well-formed */
+ for (int i = 0; i < 18; i++) {
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ }
+
+ /* 19th has an oversized header */
+ assertEqualInt(ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.lzh.uu b/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.lzh.uu
new file mode 100644
index 000000000000..e562e440a777
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_lha_oversize_header.lzh.uu
@@ -0,0 +1,60 @@
+begin 644 test_read_format_lha_oversize_header.lzh
+M)L`M;&AD+0```````````$@B[!``!&1I<EP``%4`@5$!`.A!Z0/I`R?X+6QH
+M9"T```````````!((NP0``5D:7(R7```50"!40$`[4'I`^D#.'PM;&AD+0``
+M`````````4@C[!``%F1I<C)<<WEM;&EN:S%\+BY<9FEL93$``%4``J,"`.VA
+MZ0/I`SA^+6QH9"T```````````%((^P0`!9D:7(R7'-Y;6QI;FLR?"XN7&9I
+M;&4R``!5``*C`@#MH>D#Z0,GO2UL:#4M(@```#P`````2"+L(``%9FEL93&D
+MYU4`@5$!`*2!Z0/I`P`80FYIQ>/Z`=-:'>9%#"P%J!\CH0"/GE$,.W6FMSD%
+M*_4G02UL:#4M(@```$X`````2"+L(``%9FEL93+5%54`@5$!`+:!Z0/I`P`8
+M0FYIQV/Z`=.:'.9%#"P%J-\+H0"/'E$,.W6FMSD%*_T9?RUL:&0M&@``````
+M````2"+L(`$```!5!P`"9&ER_P4`4.A!!P!1Z0/I`P<`5(%1`0```!F!+6QH
+M9"T;``````````!((NP@`0```%4(``)D:7(R_P4`4.U!!P!1Z0/I`P<`5(%1
+M`0```!YQ+6QH9"TG``````````%((^P@`05F:6QE,0``510``F1I<C+_<WEM
+M;&EN:S%\+B[_!0!0[:$'`%'I`^D#!P!4`J,"````'G(M;&AD+2<`````````
+M`4@C["`!!69I;&4R``!5%``"9&ER,O]S>6UL:6YK,GPN+O\%`%#MH0<`4>D#
+MZ0,'`%0"HP(````>!BUL:#4M-0```#P`````2"+L(`$%9FEL93&DYU4%`%"D
+M@0<`4>D#Z0,'`%2!40$`````&$)N:<7C^@'36AWF10PL!:@?(Z$`CYY1##MU
+MIK<Y!2OU'G@M;&@U+34```!.`````$@B["`!!69I;&4RU155!0!0MH$'`%'I
+M`^D#!P!4@5$!`````!A";FG'8_H!TYH<YD4,+`6HWPNA`(\>40P[=::W.04K
+M_34`+6QH9"T``````````(%1`0`@`@``504```7>`P`!!P`"9&ER_P4`4.A!
+M!P!1Z0/I`P``-@`M;&AD+0``````````@5$!`"`"``!5!0``")D#``$(``)D
+M:7(R_P4`4.U!!P!1Z0/I`P``1P`M;&AD+0```````````J,"`"`"``!5!0``
+MM*D(``%F:6QE,10``F1I<C+_<WEM;&EN:S%\+B[_!0!0[:$'`%'I`^D#``!'
+M`"UL:&0M```````````"HP(`(`(``%4%``"'[0@``69I;&4R%``"9&ER,O]S
+M>6UL:6YK,GPN+O\%`%#MH0<`4>D#Z0,``#,`+6QH-2TB````/````(%1`0`@
+M`J3G504``/T!"``!9FEL93$%`%"D@0<`4>D#Z0,````80FYIQ>/Z`=-:'>9%
+M#"P%J!\CH0"/GE$,.W6FMSD%*_4S`"UL:#4M(@```$X```"!40$`(`+5%54%
+M```OQ@@``69I;&4R!0!0MH$'`%'I`^D#````&$)N:<=C^@'3FASF10PL!:C?
+M"Z$`CQY1##MUIK<Y!2O]!``M;&AD+0``````````@5$!`"`#``!-?/[___\`
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````$:D`P``!0````$)`````F1I
+M<O\'````0!``&0```/_M00`````````````,%?Y,1!K^3!T```!!S-]1M/Z5
+MRP&`UD``J+*=`0!Z#]`!ELL!"``````NKP<`````!``M;&AD+0``````````
+M@5$!`"`#``!-?0````D```!&I`,```4````!"@````)D:7(R_P<```!`$``9
+M````_^U!`````````````"$:_DQ$&OY,'0```$%*@6Z[`9;+`8#60`"HLIT!
+M`'H/T`&6RP$(`````'T7!P`````$`"UL:#4M)````#P```"!40$`(`.DYTUQ
+M````"0```$:D`P``"@````%F:6QE,1D```#_I($`````````````QQ3^3",:
+M_DP=````031"HHK^E<L!@-9``*BRG0'^<J2\`9;+`0@`````"O$'```````9
+M0FV1J+1V@'IK0ZGID/-H#J@6.H0"'V^-&353:F^XA-UZ@`0`+6QH-2TD````
+M3@```(%1`0`@`]4537$````)````1J0#```*`````69I;&4R&0```/^D@0``
+M```````````A&OY,(QK^3!T```!!_D5SNP&6RP&`UD``J+*=`5C5IKP!ELL!
+M"`````!!W@<``````!E";9&H[':`>G-#B>F0\V@.J,8:A`(>;XT9-5-J;[B$
+MW7Z`-0`M;&AD+0``````````@5$!`"`"``!5!40:_DP=````0<S?4;3^E<L!
+M@-9``*BRG0$`>@_0`9;+`0@`````+J\'``````0`+6QH9"T``````````(%1
+M`0`@`P``37T````)````1J0#```%`````0H````"9&ER,O\'````0!``&0``
+M`/_M00`````````````A&OY,1!K^3!T```!!2H%NNP&6RP&`UD``J+*=`0!Z
+M#]`!ELL!"`````!]%P<`````!``M;&@U+20````\````@5$!`"`#I.=-<0``
+M``D```!&I`,```H````!9FEL93$9````_Z2!`````````````,<4_DPC&OY,
+M'0```$$T0J**_I7+`8#60`"HLIT!_G*DO`&6RP$(``````KQ!P``````&4)M
+MD:BT=H!Z:T.IZ9!1Z0/I`P```FUK=XVV['"Z\`>$(6[`2-EZ5P&P^!J*0MPV
+M*IM]&^L37/)<Y+,0WC;RYN647;X'R_\P`E;2>[SD(6GCE`4$+,RA<%QVYXC%
+M'H1Q`I@_B\H/L@(+W3`0@5CY6X%Q%WW+@7<`8(F7=77U]%5YG?`;3N&I:_6Y
+MWA$,3F@?:)>FZ!<!"?.:#C/:',\PG@>:WRP0:TH8%&:/A#VMNUUD[D?+:#XK
+M%3NMR(PSF4;V"^\X0/E3JMRQ46+7\I]S@7ZJNTX[ROSD+=\7\.O#I:W._S>P
+M-B(W4`=N<T/$OT+5];U>*!<4AM0Y4$L'"(=02P,$\$*.O%TM;&@U+0``KPLX
+M+4``2@````````"``````/________\`````````V0````"A````````````
+M````````````+6QH-RT```!8$,P.0&OJER*:CF[S6/=2*T4J8HB@[:HP^'7*
+MK?(\7:Y+-"U'*^OXT210`,J%*>9(T9[(@N(DJ+A)A```3`]%Z8U.^3!F0_G-
+17YFUYB_%6DWMSZ8'\Z-^CR@`
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.c b/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.c
new file mode 100644
index 000000000000..77dd78cccc48
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2026 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) 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 "test.h"
+
+DEFINE_TEST(test_read_format_rar5_loop_bug)
+{
+ const char *reffile = "test_read_format_rar5_loop_bug.rar";
+ struct archive_entry *ae;
+ struct archive *a;
+ const void *buf;
+ size_t size;
+ la_int64_t offset;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, reffile, 10240));
+
+ // This has just one entry
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+
+ // Read blocks until the end of the entry
+ while (ARCHIVE_OK == archive_read_data_block(a, &buf, &size, &offset)) {
+ }
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_free(a));
+}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.rar.uu b/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.rar.uu
new file mode 100644
index 000000000000..3e470043fb70
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_rar5_loop_bug.rar.uu
@@ -0,0 +1,189 @@
+begin 644 test_read_format_rar5_loop_bug.rar
+M4F%R(1H'`0#%&C,R`P$``)T-9%L.`@+P0`"`@`P`@`,``6'(WFP@`?\7_U/^
+M8@!.`B`H````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+5```````````````````Y^;*!`@4`
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_read_set_format.c b/contrib/libarchive/libarchive/test/test_read_set_format.c
index 615dd8ae8022..2ea39045201f 100644
--- a/contrib/libarchive/libarchive/test/test_read_set_format.c
+++ b/contrib/libarchive/libarchive/test/test_read_set_format.c
@@ -177,6 +177,40 @@ DEFINE_TEST(test_read_append_wrong_filter)
assertEqualInt(ARCHIVE_OK,archive_read_free(a));
}
+DEFINE_TEST(test_read_append_lzop_filter)
+{
+ struct archive *a;
+ int r;
+
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+ r = archive_read_append_filter(a, ARCHIVE_FILTER_LZOP);
+ if (archive_liblzo2_version() != NULL) {
+ assertEqualIntA(a, ARCHIVE_OK, r);
+ } else if (canLzop()) {
+ // We're using an external program
+ assertEqualIntA(a, ARCHIVE_WARN, r);
+ }
+
+ archive_read_free(a);
+}
+
+DEFINE_TEST(test_read_append_grzip_filter)
+{
+ struct archive *a;
+ int r;
+
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR));
+ r = archive_read_append_filter(a, ARCHIVE_FILTER_GRZIP);
+ // Grzip currently always uses an external program.
+ if (canGrzip()) {
+ assertEqualIntA(a, ARCHIVE_WARN, r);
+ }
+
+ archive_read_free(a);
+}
+
DEFINE_TEST(test_read_append_filter_program)
{
struct archive_entry *ae;
diff --git a/contrib/libarchive/libarchive/test/test_ustar_filename_encoding.c b/contrib/libarchive/libarchive/test/test_ustar_filename_encoding.c
index 1242bd1d3cd2..440d1b99276e 100644
--- a/contrib/libarchive/libarchive/test/test_ustar_filename_encoding.c
+++ b/contrib/libarchive/libarchive/test/test_ustar_filename_encoding.c
@@ -492,3 +492,43 @@ DEFINE_TEST(test_ustar_filename_encoding_UTF16_win)
assertEqualMem(buff + 157, "\xE8\xA1\xA8.txt", 7);
#endif
}
+
+DEFINE_TEST(test_ustar_filename_encoding_fail_UTF16_win)
+{
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ skipping("This test is meant to verify unicode string handling"
+ " on Windows with UTF-16 names");
+ return;
+#else
+ struct archive *a;
+ struct archive_entry *entry;
+ char buff[4096];
+ size_t used;
+
+ /* Test the C locale by not calling setlocale. */
+
+ a = archive_write_new();
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_ustar(a));
+ if (archive_write_set_options(a, "hdrcharset=CP437") != ARCHIVE_OK) {
+ skipping("This system cannot convert character-set"
+ " from UTF-16 to CP437.");
+ archive_write_free(a);
+ return;
+ }
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ entry = archive_entry_new2(a);
+ /* Set the filename using a UTF-16 string */
+ archive_entry_copy_pathname_w(entry, L"\u8868.txt");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, entry));
+ /* The pathname cannot even be represented in the current locale
+ for inclusion in the error message. */
+ assertEqualString("Can't translate pathname to CP437",
+ archive_error_string(a));
+ archive_entry_free(entry);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+#endif
+}
diff --git a/contrib/libarchive/libarchive/test/test_v7tar_filename_encoding.c b/contrib/libarchive/libarchive/test/test_v7tar_filename_encoding.c
new file mode 100644
index 000000000000..96594b12180e
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_v7tar_filename_encoding.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2003-2025 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) 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 "test.h"
+
+#include <locale.h>
+
+DEFINE_TEST(test_v7tar_filename_encoding_fail_UTF16_win)
+{
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ skipping("This test is meant to verify unicode string handling"
+ " on Windows with UTF-16 names");
+ return;
+#else
+ struct archive *a;
+ struct archive_entry *entry;
+ char buff[4096];
+ size_t used;
+
+ /* Test the C locale by not calling setlocale. */
+
+ a = archive_write_new();
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_v7tar(a));
+ if (archive_write_set_options(a, "hdrcharset=CP437") != ARCHIVE_OK) {
+ skipping("This system cannot convert character-set"
+ " from UTF-16 to CP437.");
+ archive_write_free(a);
+ return;
+ }
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ entry = archive_entry_new2(a);
+ /* Set the filename using a UTF-16 string */
+ archive_entry_copy_pathname_w(entry, L"\u8868.txt");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, entry));
+ /* The pathname cannot even be represented in the current locale
+ for inclusion in the error message. */
+ assertEqualString("Can't translate pathname to CP437",
+ archive_error_string(a));
+ archive_entry_free(entry);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+#endif
+}
diff --git a/contrib/libarchive/libarchive/test/test_warn_missing_hardlink_target.c b/contrib/libarchive/libarchive/test/test_warn_missing_hardlink_target.c
index d7fa5eb6c0fc..b3e5b95f06d4 100644
--- a/contrib/libarchive/libarchive/test/test_warn_missing_hardlink_target.c
+++ b/contrib/libarchive/libarchive/test/test_warn_missing_hardlink_target.c
@@ -37,7 +37,7 @@ DEFINE_TEST(test_warn_missing_hardlink_target)
assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
assertEqualInt(ENOENT, archive_errno(a));
- assertEqualString("Hard-link target 'hardlink-target' does not exist.",
+ assertEqualString("Hard-link target 'hardlink-target' does not exist",
archive_error_string(a));
archive_entry_free(ae);
diff --git a/contrib/libarchive/libarchive/test/test_write_disk.c b/contrib/libarchive/libarchive/test/test_write_disk.c
index fa0743f949f6..1e503cdb178a 100644
--- a/contrib/libarchive/libarchive/test/test_write_disk.c
+++ b/contrib/libarchive/libarchive/test/test_write_disk.c
@@ -233,6 +233,22 @@ static void create_reg_file_win(struct archive_entry *ae, const char *msg)
assertEqualInt(st.st_size, sizeof(data));
free(fname);
}
+#else
+static void create_fail(struct archive_entry *ae, int experr,
+ const char *msg)
+{
+ struct archive *ad;
+ struct stat st;
+
+ /* Write the entry to disk. */
+ assert((ad = archive_write_disk_new()) != NULL);
+ failure("%s", msg);
+ assertEqualIntA(ad, ARCHIVE_FAILED, archive_write_header(ad, ae));
+ assertEqualIntA(ad, experr, archive_errno(ad));
+ assertEqualInt(0, archive_write_free(ad));
+ assertEqualInt(-1, stat(archive_entry_pathname(ae), &st));
+ assertEqualInt(ENOENT, errno);
+}
#endif /* _WIN32 && !__CYGWIN__ */
DEFINE_TEST(test_write_disk)
@@ -344,5 +360,18 @@ DEFINE_TEST(test_write_disk)
" with unusable characters in its file name");
archive_entry_free(ae);
free(fullpath);
+#else /* !_WIN32 || __CYGWIN__ */
+ /* A directory with a /../ in the middle */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "a/b/../b/file");
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ /* First attempt should fail with EACCES */
+ assertEqualInt(0, mkdir("a", 0111));
+ create_fail(ae, EACCES,
+ "Test failing to create parent directory with /../");
+ /* Now let it succeed */
+ assertEqualInt(0, chmod("a", 0755));
+ create(ae, "Test creating parent directory with /../");
+ archive_entry_free(ae);
#endif /* _WIN32 && !__CYGWIN__ */
}
diff --git a/contrib/libarchive/libarchive/test/test_write_disk_perms.c b/contrib/libarchive/libarchive/test/test_write_disk_perms.c
index 72b55179d86e..4f246e9bc2f0 100644
--- a/contrib/libarchive/libarchive/test/test_write_disk_perms.c
+++ b/contrib/libarchive/libarchive/test/test_write_disk_perms.c
@@ -26,6 +26,13 @@
#if !defined(_WIN32) || defined(__CYGWIN__)
+#ifdef HAVE_GETEUID
+#define getuid() geteuid()
+#endif
+#ifdef HAVE_GETEGID
+#define getgid() getegid()
+#endif
+
#define UMASK 022
static long _default_gid = -1;
@@ -142,7 +149,7 @@ DEFINE_TEST(test_write_disk_perms)
* and we're on a system where group ownership is inherited.
* (Because we're not allowed to SGID files with defaultgid().)
*/
- assertEqualInt(0, chown(".", getuid(), getgid()));
+ assertChown(".", getuid(), getgid());
/* Create an archive_write_disk object. */
assert((a = archive_write_disk_new()) != NULL);
@@ -208,7 +215,7 @@ DEFINE_TEST(test_write_disk_perms)
if (getuid() == 0) {
original_uid = getuid() + 1;
try_to_change_uid = getuid();
- assertEqualInt(0, chown("dir_owner", original_uid, getgid()));
+ assertChown("dir_owner", original_uid, getgid());
} else {
original_uid = getuid();
try_to_change_uid = getuid() + 1;
diff --git a/contrib/libarchive/libarchive/test/test_zip_filename_encoding.c b/contrib/libarchive/libarchive/test/test_zip_filename_encoding.c
index b6786f2c3b18..1cb394547066 100644
--- a/contrib/libarchive/libarchive/test/test_zip_filename_encoding.c
+++ b/contrib/libarchive/libarchive/test/test_zip_filename_encoding.c
@@ -622,3 +622,43 @@ DEFINE_TEST(test_zip_filename_encoding_UTF16_win)
/* NOTE: ZIP does not support hardlinks */
#endif
}
+
+DEFINE_TEST(test_zip_filename_encoding_fail_UTF16_win)
+{
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ skipping("This test is meant to verify unicode string handling"
+ " on Windows with UTF-16 names");
+ return;
+#else
+ struct archive *a;
+ struct archive_entry *entry;
+ char buff[4096];
+ size_t used;
+
+ /* Test the C locale by not calling setlocale. */
+
+ a = archive_write_new();
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_zip(a));
+ if (archive_write_set_options(a, "hdrcharset=CP437") != ARCHIVE_OK) {
+ skipping("This system cannot convert character-set"
+ " from UTF-16 to CP437.");
+ archive_write_free(a);
+ return;
+ }
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ entry = archive_entry_new2(a);
+ /* Set the filename using a UTF-16 string */
+ archive_entry_copy_pathname_w(entry, L"\u8868.txt");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, entry));
+ /* The pathname cannot even be represented in the current locale
+ for inclusion in the error message. */
+ assertEqualString("Can't translate pathname to CP437",
+ archive_error_string(a));
+ archive_entry_free(entry);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+#endif
+}
diff --git a/contrib/libarchive/unzip/la_getline.c b/contrib/libarchive/libarchive_fe/lafe_getline.c
index 40ab35cde842..ebd9f1a894a7 100644
--- a/contrib/libarchive/unzip/la_getline.c
+++ b/contrib/libarchive/libarchive_fe/lafe_getline.c
@@ -7,12 +7,15 @@
* All rights reserved.
*/
-#include "bsdunzip_platform.h"
+#include "lafe_platform.h"
#ifndef HAVE_GETLINE
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
@@ -26,8 +29,10 @@
#include <string.h>
#endif
+#include "lafe_getline.h"
+
static ssize_t
-la_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+lafe_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
{
char *ptr, *eptr;
@@ -72,6 +77,6 @@ la_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
ssize_t
getline(char **buf, size_t *bufsiz, FILE *fp)
{
- return la_getdelim(buf, bufsiz, '\n', fp);
+ return lafe_getdelim(buf, bufsiz, '\n', fp);
}
#endif
diff --git a/contrib/libarchive/unzip/la_getline.h b/contrib/libarchive/libarchive_fe/lafe_getline.h
index d7588731943d..b10641ffd1eb 100644
--- a/contrib/libarchive/unzip/la_getline.h
+++ b/contrib/libarchive/libarchive_fe/lafe_getline.h
@@ -5,12 +5,19 @@
* All rights reserved.
*/
-#ifndef LA_GETLINE_H_INCLUDED
-#define LA_GETLINE_H_INCLUDED
+#ifndef LAFE_GETLINE_H_INCLUDED
+#define LAFE_GETLINE_H_INCLUDED
+
+#include "lafe_platform.h"
-#include <stdio.h>
#ifndef HAVE_GETLINE
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
#endif
-#endif /* !LA_GETLINE_H_INCLUDED */
+#endif /* !LAFE_GETLINE_H_INCLUDED */
diff --git a/contrib/libarchive/tar/bsdtar.c b/contrib/libarchive/tar/bsdtar.c
index 92e86fd6bd94..fbc0627a83b9 100644
--- a/contrib/libarchive/tar/bsdtar.c
+++ b/contrib/libarchive/tar/bsdtar.c
@@ -525,28 +525,28 @@ main(int argc, char **argv)
if (archive_match_include_date(bsdtar->matching,
ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_NEWER_CTIME_THAN:
if (archive_match_include_file_time(bsdtar->matching,
ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_NEWER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_NEWER_MTIME: /* GNU tar */
if (archive_match_include_date(bsdtar->matching,
ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_NEWER_MTIME_THAN:
if (archive_match_include_file_time(bsdtar->matching,
ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_NEWER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_NODUMP: /* star */
@@ -618,28 +618,28 @@ main(int argc, char **argv)
if (archive_match_include_date(bsdtar->matching,
ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_OLDER_CTIME_THAN:
if (archive_match_include_file_time(bsdtar->matching,
ARCHIVE_MATCH_CTIME | ARCHIVE_MATCH_OLDER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_OLDER_MTIME:
if (archive_match_include_date(bsdtar->matching,
ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_OLDER_MTIME_THAN:
if (archive_match_include_file_time(bsdtar->matching,
ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER,
bsdtar->argument) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case OPTION_ONE_FILE_SYSTEM: /* GNU tar */
@@ -819,7 +819,7 @@ main(int argc, char **argv)
if (archive_match_exclude_pattern_from_file(
bsdtar->matching, bsdtar->argument, 0)
!= ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
break;
case 'x': /* SUSv2 */
@@ -1026,7 +1026,7 @@ main(int argc, char **argv)
if (bsdtar->return_value != 0)
lafe_warnc(0,
- "Error exit delayed from previous errors.");
+ "Error exit delayed from previous errors");
return (bsdtar->return_value);
}
diff --git a/contrib/libarchive/tar/read.c b/contrib/libarchive/tar/read.c
index 7cbcfb19ff0a..4acbf6b2a9dc 100644
--- a/contrib/libarchive/tar/read.c
+++ b/contrib/libarchive/tar/read.c
@@ -255,7 +255,7 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer)
break;
p = archive_entry_pathname(entry);
if (p == NULL || p[0] == '\0') {
- lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping.");
+ lafe_warnc(0, "Archive entry has empty or unreadable filename ... skipping");
bsdtar->return_value = 1;
continue;
}
diff --git a/contrib/libarchive/tar/util.c b/contrib/libarchive/tar/util.c
index 6e41e49de133..6b21e8aeff2b 100644
--- a/contrib/libarchive/tar/util.c
+++ b/contrib/libarchive/tar/util.c
@@ -715,8 +715,8 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
/* Use uname if it's present, else uid. */
p = archive_entry_uname(entry);
if ((p == NULL) || (*p == '\0')) {
- snprintf(tmp, sizeof(tmp), "%lu ",
- (unsigned long)archive_entry_uid(entry));
+ snprintf(tmp, sizeof(tmp), "%lld ",
+ (long long)archive_entry_uid(entry));
p = tmp;
}
w = strlen(p);
@@ -730,8 +730,8 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
fprintf(out, "%s", p);
w = strlen(p);
} else {
- snprintf(tmp, sizeof(tmp), "%lu",
- (unsigned long)archive_entry_gid(entry));
+ snprintf(tmp, sizeof(tmp), "%lld",
+ (long long)archive_entry_gid(entry));
w = strlen(tmp);
fprintf(out, "%s", tmp);
}
diff --git a/contrib/libarchive/tar/write.c b/contrib/libarchive/tar/write.c
index b39a397707ba..abff3831bcda 100644
--- a/contrib/libarchive/tar/write.c
+++ b/contrib/libarchive/tar/write.c
@@ -301,7 +301,7 @@ tar_mode_r(struct bsdtar *bsdtar)
archive_read_free(a);
close(bsdtar->fd);
lafe_errc(1, 0,
- "Cannot append to compressed archive.");
+ "Cannot append to compressed archive");
}
/* Keep going until we hit end-of-archive */
format = archive_format(a);
@@ -329,7 +329,7 @@ tar_mode_r(struct bsdtar *bsdtar)
if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK)
&& format != ARCHIVE_FORMAT_EMPTY) {
lafe_errc(1, 0,
- "Format %s is incompatible with the archive %s.",
+ "Format %s is incompatible with the archive %s",
cset_get_format(bsdtar->cset), bsdtar->filename);
}
} else {
@@ -394,12 +394,12 @@ tar_mode_u(struct bsdtar *bsdtar)
archive_read_free(a);
close(bsdtar->fd);
lafe_errc(1, 0,
- "Cannot append to compressed archive.");
+ "Cannot append to compressed archive");
}
if (archive_match_exclude_entry(bsdtar->matching,
ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_OLDER |
ARCHIVE_MATCH_EQUAL, entry) != ARCHIVE_OK)
- lafe_errc(1, 0, "Error : %s",
+ lafe_errc(1, 0, "%s",
archive_error_string(bsdtar->matching));
/* Record the last format determination we see */
format = archive_format(a);
@@ -786,7 +786,7 @@ copy_file_data_block(struct bsdtar *bsdtar, struct archive *a,
* continue. */
lafe_warnc(0,
"%s: Truncated write; file may "
- "have grown while being archived.",
+ "have grown while being archived",
archive_entry_pathname(entry));
return (0);
}
@@ -805,7 +805,7 @@ copy_file_data_block(struct bsdtar *bsdtar, struct archive *a,
/* Write was truncated; warn but continue. */
lafe_warnc(0,
"%s: Truncated write; file may have grown "
- "while being archived.",
+ "while being archived",
archive_entry_pathname(entry));
return (0);
}
@@ -1052,14 +1052,14 @@ test_for_append(struct bsdtar *bsdtar)
if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
lafe_errc(1, 0, "no files or directories specified");
if (bsdtar->filename == NULL)
- lafe_errc(1, 0, "Cannot append to stdout.");
+ lafe_errc(1, 0, "Cannot append to stdout");
if (stat(bsdtar->filename, &s) != 0)
return;
if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
lafe_errc(1, 0,
- "Cannot append to %s: not a regular file.",
+ "Cannot append to %s: not a regular file",
bsdtar->filename);
/* Is this an appropriate check here on Windows? */
diff --git a/contrib/libarchive/test_utils/test_common.h b/contrib/libarchive/test_utils/test_common.h
index a9df300e3be8..2f19597dee6b 100644
--- a/contrib/libarchive/test_utils/test_common.h
+++ b/contrib/libarchive/test_utils/test_common.h
@@ -163,6 +163,9 @@
/* change file/directory permissions and errors if it fails */
#define assertChmod(pathname, mode) \
assertion_chmod(__FILE__, __LINE__, pathname, mode)
+/* change file/directory ownership and errors if it fails */
+#define assertChown(pathname, user, group) \
+ assertion_chown(__FILE__, __LINE__, pathname, user, group)
/* Assert two files have the same file flags */
#define assertEqualFflags(patha, pathb) \
assertion_compare_fflags(__FILE__, __LINE__, patha, pathb, 0)
@@ -279,6 +282,7 @@ void failure(const char *fmt, ...) __LA_PRINTFLIKE(1, 2);
int assertion_assert(const char *, int, int, const char *, void *);
int assertion_chdir(const char *, int, const char *);
int assertion_chmod(const char *, int, const char *, int);
+int assertion_chown(const char *, int, const char *, int, int);
int assertion_compare_fflags(const char *, int, const char *, const char *,
int);
int assertion_empty_file(const char *, int, const char *);
diff --git a/contrib/libarchive/test_utils/test_main.c b/contrib/libarchive/test_utils/test_main.c
index dbd3fcf60e9a..1f641bbe8db7 100644
--- a/contrib/libarchive/test_utils/test_main.c
+++ b/contrib/libarchive/test_utils/test_main.c
@@ -96,6 +96,12 @@ extern char **environ;
# define USE_POSIX_SPAWN 1
# endif
#endif
+#if !defined(_WIN32)
+# if HAVE_PWD_H && HAVE_GETEUID && HAVE_GETEGID
+# include <pwd.h>
+# define RUN_TEST_UNPRIV 1
+# endif
+#endif
#ifndef nitems
#define nitems(arr) (sizeof(arr) / sizeof((arr)[0]))
@@ -179,6 +185,14 @@ const char *testprogfile;
const char *testprog;
#endif
+#ifdef RUN_TEST_UNPRIV
+/* Unprivileged user to run as */
+const char *tuser = "nobody";
+/* Original and test credentials */
+uid_t ouid, tuid;
+uid_t ogid, tgid;
+#endif
+
#if defined(_WIN32) && !defined(__CYGWIN__)
static void *GetFunctionKernel32(const char *);
static int my_CreateSymbolicLinkA(const char *, const char *, int);
@@ -632,6 +646,22 @@ assertion_chmod(const char *file, int line, const char *pathname, int mode)
}
+/* change file/directory ownership and errors if it fails */
+int
+assertion_chown(const char *file, int line, const char *pathname, int user,
+ int group)
+{
+ assertion_count(file, line);
+#ifdef HAVE_CHOWN
+ if (chown(pathname, (uid_t)user, (gid_t)group) == 0)
+ return (1);
+#endif
+ failure_start(file, line, "chown(\"%s\", %d, %d)", pathname,
+ user, group);
+ failure_finish(NULL);
+ return (0);
+}
+
/* Verify two integers are equal. */
int
assertion_equal_int(const char *file, int line,
@@ -3610,8 +3640,11 @@ test_run(int i, const char *tmpdir)
exit(1);
}
testworkdir = workdir;
- if (!assertMakeDir(testworkdir, 0755)
- || !assertChdir(testworkdir)) {
+ if (!assertMakeDir(testworkdir, 0755) ||
+#ifdef RUN_TEST_UNPRIV
+ (tuser != NULL && !assertChown(testworkdir, tuid, tgid)) ||
+#endif
+ !assertChdir(testworkdir)) {
fprintf(stderr,
"ERROR: Can't chdir to work dir %s\n", testworkdir);
exit(1);
@@ -3620,10 +3653,28 @@ test_run(int i, const char *tmpdir)
set_c_locale();
/* Record the umask before we run the test. */
umask(oldumask = umask(0));
+#ifdef RUN_TEST_UNPRIV
+ /*
+ * Temporarily drop privileges.
+ */
+ if (tuser != NULL) {
+ (void)setegid(tuid);
+ (void)seteuid(tuid);
+ }
+#endif
/*
* Run the actual test.
*/
(*tests[i].func)();
+#ifdef RUN_TEST_UNPRIV
+ /*
+ * Restore original credentials.
+ */
+ if (tuser != NULL) {
+ (void)seteuid(ouid);
+ (void)setegid(ogid);
+ }
+#endif
/*
* Clean up and report afterwards.
*/
@@ -3949,6 +4000,9 @@ main(int argc, char **argv)
#endif
char *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL;
char tmpdir_timestamp[32];
+#ifdef RUN_TEST_UNPRIV
+ struct passwd *pw;
+#endif
(void)argc; /* UNUSED */
@@ -4110,6 +4164,11 @@ main(int argc, char **argv)
case 's':
fail_if_tests_skipped = 1;
break;
+#ifdef RUN_TEST_UNPRIV
+ case 'U':
+ tuser = optarg;
+ break;
+#endif
case 'u':
until_failure++;
break;
@@ -4194,6 +4253,28 @@ main(int argc, char **argv)
}
#endif
+#ifdef RUN_TEST_UNPRIV
+ /*
+ * Check if we are root, and get user to run as.
+ */
+ ouid = getuid();
+ ogid = getgid();
+ if (ouid == 0) {
+ if ((pw = getpwnam(tuser)) == NULL) {
+ fprintf(stderr, "ERROR: Unknown user %s\n", tuser);
+ exit(1);
+ }
+ tuid = pw->pw_uid;
+ tgid = pw->pw_gid;
+ printf("Will switch to user %s (uid %d gid %d)\n", tuser,
+ tuid, tgid);
+ } else {
+ tuser = NULL;
+ tuid = ouid;
+ tgid = ogid;
+ }
+#endif
+
/*
* Create a temp directory for the following tests.
* Include the time the tests started as part of the name,
diff --git a/contrib/libarchive/unzip/bsdunzip.c b/contrib/libarchive/unzip/bsdunzip.c
index 14bd418f169c..aa849568ba7e 100644
--- a/contrib/libarchive/unzip/bsdunzip.c
+++ b/contrib/libarchive/unzip/bsdunzip.c
@@ -9,7 +9,7 @@
#include "bsdunzip_platform.h"
#include "la_queue.h"
-#include "la_getline.h"
+#include "lafe_getline.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -884,9 +884,9 @@ list(struct archive *a, struct archive_entry *e)
mtime = archive_entry_mtime(e);
tm = localtime(&mtime);
if (*y_str)
- strftime(buf, sizeof(buf), "%m-%d-%G %R", tm);
+ strftime(buf, sizeof(buf), "%m-%d-%Y %R", tm);
else
- strftime(buf, sizeof(buf), "%m-%d-%g %R", tm);
+ strftime(buf, sizeof(buf), "%m-%d-%y %R", tm);
pathname = archive_entry_pathname(e);
if (!pathname)
diff --git a/include/unistd.h b/include/unistd.h
index 21e3a7740607..797eac4c364d 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -453,6 +453,10 @@ int unlinkat(int, const char *, int);
int symlink(const char * __restrict, const char * __restrict);
#endif
+#if __POSIX_VISIBLE >= 202405
+pid_t _Fork(void);
+#endif
+
/* X/Open System Interfaces */
#if __XSI_VISIBLE
char *crypt(const char *, const char *);
@@ -594,7 +598,6 @@ int undelete(const char *);
int unwhiteout(const char *);
void *valloc(size_t); /* obsoleted by malloc() */
int funlinkat(int, const char *, int, int);
-pid_t _Fork(void);
#ifndef _OPTRESET_DECLARED
#define _OPTRESET_DECLARED
diff --git a/lib/libarchive/tests/Makefile b/lib/libarchive/tests/Makefile
index 880c0d3ce0c6..3a03725054f4 100644
--- a/lib/libarchive/tests/Makefile
+++ b/lib/libarchive/tests/Makefile
@@ -131,6 +131,8 @@ TESTS_SRCS= \
test_read_format_ar.c \
test_read_format_cab.c \
test_read_format_cab_filename.c \
+ test_read_format_cab_lzx_oob.c \
+ test_read_format_cab_skip_malformed.c \
test_read_format_cpio_afio.c \
test_read_format_cpio_bin.c \
test_read_format_cpio_bin_Z.c \
@@ -159,6 +161,7 @@ TESTS_SRCS= \
test_read_format_iso_Z.c \
test_read_format_iso_multi_extent.c \
test_read_format_iso_xorriso.c \
+ test_read_format_iso_zisofs_overflow.c \
test_read_format_isorr_rr_moved.c \
test_read_format_isojoliet_bz2.c \
test_read_format_isojoliet_long.c \
@@ -172,10 +175,12 @@ TESTS_SRCS= \
test_read_format_lha_bugfix_0.c \
test_read_format_lha_filename.c \
test_read_format_lha_filename_utf16.c \
+ test_read_format_lha_oversize_header.c \
test_read_format_mtree.c \
test_read_format_mtree_crash747.c \
test_read_format_pax_bz2.c \
test_read_format_rar.c \
+ test_read_format_rar5_loop_bug.c \
test_read_format_rar5.c \
test_read_format_rar_encryption.c \
test_read_format_rar_encryption_data.c \
@@ -242,6 +247,7 @@ TESTS_SRCS= \
test_sparse_basic.c \
test_tar_filenames.c \
test_tar_large.c \
+ test_v7tar_filename_encoding.c \
test_warn_missing_hardlink_target.c \
test_ustar_filenames.c \
test_ustar_filename_encoding.c \
@@ -499,8 +505,10 @@ ${PACKAGE}FILES+= test_read_format_7zip_lzma2_riscv.7z.uu
${PACKAGE}FILES+= test_read_format_7zip_lzma2_sparc.7z.uu
${PACKAGE}FILES+= test_read_format_7zip_malformed.7z.uu
${PACKAGE}FILES+= test_read_format_7zip_malformed2.7z.uu
+${PACKAGE}FILES+= test_read_format_7zip_malformed3.7z.uu
${PACKAGE}FILES+= test_read_format_7zip_packinfo_digests.7z.uu
${PACKAGE}FILES+= test_read_format_7zip_ppmd.7z.uu
+${PACKAGE}FILES+= test_read_format_7zip_sfx_elf64trunc.elf.uu
${PACKAGE}FILES+= test_read_format_7zip_sfx_elf.elf.uu
${PACKAGE}FILES+= test_read_format_7zip_sfx_modified_pe.exe.uu
${PACKAGE}FILES+= test_read_format_7zip_sfx_pe.exe.uu
@@ -517,6 +525,8 @@ ${PACKAGE}FILES+= test_read_format_cab_1.cab.uu
${PACKAGE}FILES+= test_read_format_cab_2.cab.uu
${PACKAGE}FILES+= test_read_format_cab_3.cab.uu
${PACKAGE}FILES+= test_read_format_cab_filename_cp932.cab.uu
+${PACKAGE}FILES+= test_read_format_cab_lzx_oob.cab.uu
+${PACKAGE}FILES+= test_read_format_cab_skip_malformed.cab.uu
${PACKAGE}FILES+= test_read_format_cpio_bin_be.cpio.uu
${PACKAGE}FILES+= test_read_format_cpio_bin_le.cpio.uu
${PACKAGE}FILES+= test_read_format_cpio_filename_cp866.cpio.uu
@@ -552,6 +562,7 @@ ${PACKAGE}FILES+= test_read_format_iso_rockridge_ce.iso.Z.uu
${PACKAGE}FILES+= test_read_format_iso_rockridge_new.iso.Z.uu
${PACKAGE}FILES+= test_read_format_iso_rockridge_rr_moved.iso.Z.uu
${PACKAGE}FILES+= test_read_format_iso_xorriso.iso.Z.uu
+${PACKAGE}FILES+= test_read_format_iso_zisofs_overflow.iso.uu
${PACKAGE}FILES+= test_read_format_iso_zisofs.iso.Z.uu
${PACKAGE}FILES+= test_read_format_lha_bugfix_0.lzh.uu
${PACKAGE}FILES+= test_read_format_lha_filename_cp932.lzh.uu
@@ -563,6 +574,7 @@ ${PACKAGE}FILES+= test_read_format_lha_header3.lzh.uu
${PACKAGE}FILES+= test_read_format_lha_lh0.lzh.uu
${PACKAGE}FILES+= test_read_format_lha_lh6.lzh.uu
${PACKAGE}FILES+= test_read_format_lha_lh7.lzh.uu
+${PACKAGE}FILES+= test_read_format_lha_oversize_header.lzh.uu
${PACKAGE}FILES+= test_read_format_lha_withjunk.lzh.uu
${PACKAGE}FILES+= test_read_format_mtree.mtree.uu
${PACKAGE}FILES+= test_read_format_mtree_crash747.mtree.bz2.uu
@@ -615,6 +627,7 @@ ${PACKAGE}FILES+= test_read_format_rar5_dirdata.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_distance_overflow.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_encrypted.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_encrypted_filenames.rar.uu
+${PACKAGE}FILES+= test_read_format_rar5_loop_bug.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_solid_encrypted.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_solid_encrypted_filenames.rar.uu
${PACKAGE}FILES+= test_read_format_rar5_extra_field_version.rar.uu
diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3
index ef897c653728..75fd6307bd30 100644
--- a/lib/libc/gen/sysctl.3
+++ b/lib/libc/gen/sysctl.3
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 31, 2025
+.Dd April 15, 2026
.Dt SYSCTL 3
.Os
.Sh NAME
@@ -325,7 +325,7 @@ information.
.Bl -column "KERNXMAXFILESPERPROCXXX" "struct clockrateXXX" -offset indent
.It Sy Second Level Name Ta Sy Type Ta Sy Changeable
.It Dv KERN_ARGMAX Ta integer Ta no
-.It Dv KERN_ARND Ta integer Ta no
+.It Dv KERN_ARND Ta byte buffer Ta no
.It Dv KERN_BOOTFILE Ta string Ta yes
.It Dv KERN_BOOTTIME Ta struct timeval Ta no
.It Dv KERN_CLOCKRATE Ta struct clockinfo Ta no
@@ -333,7 +333,7 @@ information.
.It Dv KERN_HOSTID Ta integer Ta yes
.It Dv KERN_HOSTUUID Ta string Ta yes
.It Dv KERN_HOSTNAME Ta string Ta yes
-.It Dv KERN_IOV_MAX Ta integer Ta yes
+.It Dv KERN_IOV_MAX Ta integer Ta no
.It Dv KERN_JOB_CONTROL Ta integer Ta no
.It Dv KERN_LOCKF Ta struct kinfo_lockf Ta no
.It Dv KERN_LOGSIGEXIT Ta integer Ta yes
@@ -457,7 +457,7 @@ the currently installed userland.
.It Li KERN_OSRELEASE
The system release string.
.It Li KERN_OSREV
-The system revision string.
+The system revision number.
.It Li KERN_OSTYPE
The system type string.
.It Li KERN_POSIX1
@@ -501,14 +501,14 @@ specifies the current process.
.It Dv KERN_PROC_GROUPS Ta "gid_t []"
.It Dv KERN_PROC_ENV Ta "Set of strings"
.It Dv KERN_PROC_AUXV Ta "Elf_Auxinfo []"
-.It Dv KERN_PROC_RLIMIT Ta "Integer"
-.It Dv KERN_PROC_PS_STRINGS Ta "Integer"
+.It Dv KERN_PROC_RLIMIT Ta "struct rlimit"
+.It Dv KERN_PROC_PS_STRINGS Ta "Pointer to struct ps_strings"
.It Dv KERN_PROC_UMASK Ta "Integer/short"
.It Dv KERN_PROC_OSREL Ta "Integer"
-.It Dv KERN_PROC_SIGTRAMP Ta "Integer"
-.It Dv KERN_PROC_CWD Ta "String"
+.It Dv KERN_PROC_SIGTRAMP Ta "struct kinfo_sigtramp"
+.It Dv KERN_PROC_CWD Ta "struct kinfo_file"
.It Dv KERN_PROC_NFDS Ta "Integer"
-.It Dv KERN_PROC_SIGFASTBLK Ta "Integer"
+.It Dv KERN_PROC_SIGFASTBLK Ta "Address"
.It Dv KERN_PROC_VM_LAYOUT Ta "struct kinfo_vm_layout"
.It Dv KERN_PROC_RLIMIT_USAGE Ta "rlim_t []"
.It Dv KERN_PROC_KQUEUE Ta "struct kinfo_knote []"
@@ -570,7 +570,8 @@ Read from the note of the elf executable at
time.
Might be modified by the process.
.It Dv KERN_PROC_SIGTRAMP
-Address of the signal trampoline in the process address space,
+Structure describing the address range of the signal trampoline in the
+process address space,
where, simplifying, the kernel passes control for signal delivery.
.It Dv KERN_PROC_CWD
Returns the current working directory for the process.
@@ -586,6 +587,12 @@ Fills a structure describing process virtual address space layout.
Like
.Dv KERN_PROC_RLIMIT ,
but instead of the limit, returns the accounted resource usage.
+If the MIB is of the form
+.Li kern.proc.rlimit_usage\&. Ns Va pid ,
+usage values for all resources are returned.
+If the MIB is of the form
+.Li kern.proc.rlimit_usage\&. Ns Va pid Ns \&. Ns Va resource ,
+the usage value for the specified resource is returned.
For resources which do not have a meaningful current value,
.Li \-1
is returned.
diff --git a/lib/libcasper/services/cap_dns/cap_dns.c b/lib/libcasper/services/cap_dns/cap_dns.c
index 8681f0baef40..8e660b197e3a 100644
--- a/lib/libcasper/services/cap_dns/cap_dns.c
+++ b/lib/libcasper/services/cap_dns/cap_dns.c
@@ -267,7 +267,7 @@ cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
}
nvlist_destroy(nvl);
if (curai == NULL && nvlai != NULL) {
- if (firstai == NULL)
+ if (firstai != NULL)
freeaddrinfo(firstai);
return (EAI_MEMORY);
}
diff --git a/lib/libifconfig/libifconfig.c b/lib/libifconfig/libifconfig.c
index f844ae235a71..bc0fcb6021b2 100644
--- a/lib/libifconfig/libifconfig.c
+++ b/lib/libifconfig/libifconfig.c
@@ -562,7 +562,9 @@ ifconfig_create_interface(ifconfig_handle_t *h, const char *name, char **ifname)
(strncmp(name, "vlan",
strlen("vlan")) == 0) ||
(strncmp(name, "vxlan",
- strlen("vxlan")) == 0)) {
+ strlen("vxlan")) == 0) ||
+ (strncmp(name, "geneve",
+ strlen("geneve")) == 0)) {
h->error.errtype = OTHER;
h->error.errcode = ENOSYS;
return (-1);
diff --git a/lib/libifconfig/libifconfig_carp.c b/lib/libifconfig/libifconfig_carp.c
index 59faa8def496..8599470cd3f9 100644
--- a/lib/libifconfig/libifconfig_carp.c
+++ b/lib/libifconfig/libifconfig_carp.c
@@ -53,7 +53,7 @@ static struct snl_attr_parser ap_carp_get[] = {
{ .type = CARP_NL_STATE, .off = _OUT(carpr_state), .cb = snl_attr_get_uint32 },
{ .type = CARP_NL_ADVBASE, .off = _OUT(carpr_advbase), .cb = snl_attr_get_int32 },
{ .type = CARP_NL_ADVSKEW, .off = _OUT(carpr_advskew), .cb = snl_attr_get_int32 },
- { .type = CARP_NL_KEY, .off = _OUT(carpr_key), .cb = snl_attr_copy_string, .arg_u32 = CARP_KEY_LEN },
+ { .type = CARP_NL_KEY, .off = _OUT(carpr_key), .cb = snl_attr_get_bytes, .arg_u32 = CARP_KEY_LEN },
{ .type = CARP_NL_ADDR, .off = _OUT(carpr_addr), .cb = snl_attr_get_in_addr },
{ .type = CARP_NL_ADDR6, .off = _OUT(carpr_addr6), .cb = snl_attr_get_in6_addr },
{ .type = CARP_NL_VERSION, .off = _OUT(carpr_version), .cb = snl_attr_get_uint8 },
@@ -177,7 +177,8 @@ ifconfig_carp_set_info(ifconfig_handle_t *h, const char *name,
&carpr->carpr_addr);
snl_add_msg_attr(&nw, CARP_NL_ADDR6, sizeof(carpr->carpr_addr6),
&carpr->carpr_addr6);
- snl_add_msg_attr_string(&nw, CARP_NL_KEY, carpr->carpr_key);
+ snl_add_msg_attr(&nw, CARP_NL_KEY, sizeof(carpr->carpr_key),
+ carpr->carpr_key);
snl_add_msg_attr_u8(&nw, CARP_NL_VERSION, carpr->carpr_version);
snl_add_msg_attr_u8(&nw, CARP_NL_VRRP_PRIORITY, carpr->carpr_vrrp_prio);
snl_add_msg_attr_u16(&nw, CARP_NL_VRRP_ADV_INTER, carpr->carpr_vrrp_adv_inter);
diff --git a/lib/libifconfig/libifconfig_sfp.c b/lib/libifconfig/libifconfig_sfp.c
index 1ba6d231a992..a2cddf89a13a 100644
--- a/lib/libifconfig/libifconfig_sfp.c
+++ b/lib/libifconfig/libifconfig_sfp.c
@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <net/if.h>
+#include <net/cmis.h>
#include <net/sff8436.h>
#include <net/sff8472.h>
@@ -114,6 +115,50 @@ read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len,
return (0);
}
+/*
+ * Reads i2c data with CMIS page/bank selection.
+ * For upper memory (offset >= 128), the page and bank fields select
+ * which CMIS register page is mapped into the 128-255 address range.
+ */
+static int
+read_i2c_page(struct i2c_info *ii, uint8_t addr, uint8_t page, uint8_t bank,
+ uint8_t off, uint8_t len, uint8_t *buf)
+{
+ struct ifi2creq req;
+ int i, l;
+
+ if (ii->error != 0)
+ return (ii->error);
+
+ ii->ifr.ifr_data = (caddr_t)&req;
+
+ i = 0;
+ l = 0;
+ memset(&req, 0, sizeof(req));
+ req.dev_addr = addr;
+ req.offset = off;
+ req.len = len;
+ req.page = page;
+ req.bank = bank;
+
+ while (len > 0) {
+ l = MIN(sizeof(req.data), len);
+ req.len = l;
+ if (ifconfig_ioctlwrap(ii->h, AF_LOCAL, SIOCGI2CPB,
+ &ii->ifr) != 0) {
+ ii->error = errno;
+ return (errno);
+ }
+
+ memcpy(&buf[i], req.data, l);
+ len -= l;
+ i += l;
+ req.offset += l;
+ }
+
+ return (0);
+}
+
static int
i2c_info_init(struct i2c_info *ii, ifconfig_handle_t *h, const char *name)
{
@@ -193,18 +238,76 @@ get_qsfp_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp)
return (ii->error);
}
+static int
+get_cmis_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp)
+{
+ uint8_t app_desc[CMIS_APP_DESC_SIZE];
+ uint8_t dpconfig, appsel;
+ uint8_t app_off;
+
+ /* Module ID from lower memory byte 0 */
+ read_i2c(ii, CMIS_BASE, CMIS_ID, 1, &sfp->sfp_id);
+
+ /* Connector type from Page 00h byte 203 */
+ read_i2c_page(ii, CMIS_BASE, 0x00, 0,
+ CMIS_P0_CONNECTOR, 1, &sfp->sfp_conn);
+
+ /* Media type from lower memory byte 85 */
+ read_i2c(ii, CMIS_BASE, CMIS_MEDIA_TYPE, 1,
+ &sfp->sfp_cmis_media_type);
+
+ /*
+ * Read the active AppSel code from the Active Control Set
+ * (Page 11h, byte 206, bits 7:4). This tells us which
+ * Application Descriptor is actually in use.
+ * AppSel is 1-based; 0 means no application selected.
+ */
+ dpconfig = 0;
+ read_i2c_page(ii, CMIS_BASE, 0x11, 0,
+ CMIS_P11_ACS_DPCONFIG1, 1, &dpconfig);
+ appsel = (dpconfig & CMIS_ACS_APPSEL_MASK) >> CMIS_ACS_APPSEL_SHIFT;
+
+ /* Fall back to first descriptor if AppSel is 0 or out of range */
+ if (appsel == 0 || appsel > CMIS_MAX_APP_DESC)
+ appsel = 1;
+
+ /* Read the active Application Descriptor */
+ app_off = CMIS_APP_DESC_START + (appsel - 1) * CMIS_APP_DESC_SIZE;
+ read_i2c(ii, CMIS_BASE, app_off, CMIS_APP_DESC_SIZE, app_desc);
+ if (ii->error != 0)
+ return (ii->error);
+
+ /* Store MediaInterfaceID based on media type */
+ switch (sfp->sfp_cmis_media_type) {
+ case SFP_CMIS_MEDIA_TYPE_SMF:
+ sfp->sfp_cmis_smf = app_desc[CMIS_APP_MEDIA_IF_ID];
+ break;
+ case SFP_CMIS_MEDIA_TYPE_MMF:
+ sfp->sfp_cmis_mmf = app_desc[CMIS_APP_MEDIA_IF_ID];
+ break;
+ }
+
+ /* Extract media lane count from app descriptor byte 2, bits 3:0 */
+ sfp->sfp_cmis_lanes = app_desc[CMIS_APP_LANE_COUNT] & 0x0F;
+
+ return (ii->error);
+}
+
int
ifconfig_sfp_get_sfp_info(ifconfig_handle_t *h,
const char *name, struct ifconfig_sfp_info *sfp)
{
struct i2c_info ii;
- char buf[8];
+ uint8_t buf[8];
memset(sfp, 0, sizeof(*sfp));
if (i2c_info_init(&ii, h, name) != 0)
return (-1);
+ if (ifconfig_sfp_id_is_cmis(ii.id))
+ return (get_cmis_info(&ii, sfp));
+
/* Read bytes 3-10 at once */
read_i2c(&ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, buf);
if (ii.error != 0)
@@ -246,6 +349,12 @@ channel_count(enum sfp_id id)
size_t
ifconfig_sfp_channel_count(const struct ifconfig_sfp_info *sfp)
{
+ /* CMIS modules: use lane count from Application Descriptor */
+ if (ifconfig_sfp_id_is_cmis(sfp->sfp_id)) {
+ if (sfp->sfp_cmis_lanes > 0)
+ return (sfp->sfp_cmis_lanes);
+ return (0);
+ }
return (channel_count(sfp->sfp_id));
}
@@ -256,7 +365,7 @@ ifconfig_sfp_channel_count(const struct ifconfig_sfp_info *sfp)
static void
get_sff_string(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst)
{
- read_i2c(ii, addr, off, SFF_VENDOR_STRING_SIZE, dst);
+ read_i2c(ii, addr, off, SFF_VENDOR_STRING_SIZE, (uint8_t *)dst);
dst += SFF_VENDOR_STRING_SIZE;
do { *dst-- = '\0'; } while (*dst == 0x20);
}
@@ -264,7 +373,7 @@ get_sff_string(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst)
static void
get_sff_date(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst)
{
- char buf[SFF_VENDOR_DATE_SIZE];
+ uint8_t buf[SFF_VENDOR_DATE_SIZE];
read_i2c(ii, addr, off, SFF_VENDOR_DATE_SIZE, buf);
sprintf(dst, "20%c%c-%c%c-%c%c", buf[0], buf[1], buf[2], buf[3],
@@ -291,6 +400,41 @@ get_qsfp_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi)
return (ii->error);
}
+/*
+ * Read CMIS vendor strings from Page 00h (upper memory).
+ * Vendor info uses the same ASCII format as SFF-8436 but at
+ * different offsets and requires page selection.
+ */
+static void
+get_cmis_string(struct i2c_info *ii, uint8_t off, char *dst)
+{
+ read_i2c_page(ii, CMIS_BASE, 0x00, 0, off,
+ SFF_VENDOR_STRING_SIZE, dst);
+ dst += SFF_VENDOR_STRING_SIZE;
+ do { *dst-- = '\0'; } while (*dst == 0x20);
+}
+
+static void
+get_cmis_date(struct i2c_info *ii, uint8_t off, char *dst)
+{
+ char buf[SFF_VENDOR_DATE_SIZE];
+
+ read_i2c_page(ii, CMIS_BASE, 0x00, 0, off,
+ SFF_VENDOR_DATE_SIZE, buf);
+ sprintf(dst, "20%c%c-%c%c-%c%c", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5]);
+}
+
+static int
+get_cmis_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi)
+{
+ get_cmis_string(ii, CMIS_P0_VENDOR_NAME, vi->name);
+ get_cmis_string(ii, CMIS_P0_VENDOR_PN, vi->pn);
+ get_cmis_string(ii, CMIS_P0_VENDOR_SN, vi->sn);
+ get_cmis_date(ii, CMIS_P0_DATE_CODE, vi->date);
+ return (ii->error);
+}
+
int
ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h,
const char *name, struct ifconfig_sfp_vendor_info *vi)
@@ -302,6 +446,8 @@ ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h,
if (i2c_info_init(&ii, h, name) != 0)
return (-1);
+ if (ifconfig_sfp_id_is_cmis(ii.id))
+ return (get_cmis_vendor_info(&ii, vi));
if (ifconfig_sfp_id_is_qsfp(ii.id))
return (get_qsfp_vendor_info(&ii, vi));
return (get_sfp_vendor_info(&ii, vi));
@@ -386,7 +532,7 @@ get_sfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss)
uint8_t diag_type, flags;
/* Read diagnostic monitoring type */
- read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type);
+ read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, &diag_type);
if (ii->error != 0)
return (-1);
@@ -457,6 +603,47 @@ get_qsfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss)
return (ii->error);
}
+/*
+ * Read CMIS module status: temperature and voltage from lower memory,
+ * per-lane TX power, TX bias, and RX power from Page 11h Bank 0.
+ */
+static int
+get_cmis_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss,
+ size_t channels)
+{
+ /* Temperature and voltage are in lower memory (same format as SFF) */
+ ss->temp = get_sff_temp(ii, CMIS_BASE, CMIS_TEMP);
+ ss->voltage = get_sff_voltage(ii, CMIS_BASE, CMIS_VCC);
+
+ if (channels == 0)
+ return (ii->error);
+
+ ss->channel = calloc(channels, sizeof(*ss->channel));
+ if (ss->channel == NULL) {
+ ii->h->error.errtype = OTHER;
+ ii->h->error.errcode = ENOMEM;
+ return (-1);
+ }
+
+ /* Read per-lane monitors from Page 11h Bank 0 */
+ for (size_t chan = 0; chan < channels; ++chan) {
+ uint8_t off;
+ uint8_t buf[2];
+
+ /* RX optical power */
+ off = CMIS_P11_RX_PWR_1 + chan * CMIS_LANE_MON_SIZE;
+ read_i2c_page(ii, CMIS_BASE, 0x11, 0, off, 2, buf);
+ ss->channel[chan].rx = (buf[0] << 8) | buf[1];
+
+ /* TX bias current */
+ off = CMIS_P11_TX_BIAS_1 + chan * CMIS_LANE_MON_SIZE;
+ read_i2c_page(ii, CMIS_BASE, 0x11, 0, off, 2, buf);
+ ss->channel[chan].tx = (buf[0] << 8) | buf[1];
+ }
+
+ return (ii->error);
+}
+
int
ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name,
struct ifconfig_sfp_status *ss)
@@ -468,6 +655,20 @@ ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name,
if (i2c_info_init(&ii, h, name) != 0)
return (-1);
+ if (ifconfig_sfp_id_is_cmis(ii.id)) {
+ /*
+ * For CMIS, we need the lane count from the module info.
+ * Read the first Application Descriptor to get it.
+ */
+ uint8_t app_desc[CMIS_APP_DESC_SIZE];
+ size_t channels;
+
+ read_i2c(&ii, CMIS_BASE, CMIS_APP_DESC_START,
+ CMIS_APP_DESC_SIZE, app_desc);
+ channels = app_desc[CMIS_APP_LANE_COUNT] & 0x0F;
+ return (get_cmis_status(&ii, ss, channels));
+ }
+
if (ifconfig_sfp_id_is_qsfp(ii.id))
return (get_qsfp_status(&ii, ss));
return (get_sfp_status(&ii, ss));
@@ -527,6 +728,21 @@ const char *
ifconfig_sfp_physical_spec(const struct ifconfig_sfp_info *sfp,
const struct ifconfig_sfp_info_strings *strings)
{
+ /* CMIS modules: look up media interface ID based on media type */
+ if (ifconfig_sfp_id_is_cmis(sfp->sfp_id)) {
+ switch (sfp->sfp_cmis_media_type) {
+ case SFP_CMIS_MEDIA_TYPE_SMF:
+ if (strings->sfp_cmis_smf != NULL)
+ return (strings->sfp_cmis_smf);
+ break;
+ case SFP_CMIS_MEDIA_TYPE_MMF:
+ if (strings->sfp_cmis_mmf != NULL)
+ return (strings->sfp_cmis_mmf);
+ break;
+ }
+ return ("Unknown");
+ }
+
switch (sfp->sfp_id) {
case SFP_ID_UNKNOWN:
break;
@@ -557,12 +773,19 @@ ifconfig_sfp_get_sfp_dump(ifconfig_handle_t *h, const char *name,
struct i2c_info ii;
uint8_t *buf = dump->data;
- memset(dump->data, 0, sizeof(dump->data));
+ memset(buf, 0, sizeof(dump->data));
if (i2c_info_init(&ii, h, name) != 0)
return (-1);
- if (ifconfig_sfp_id_is_qsfp(ii.id)) {
+ if (ifconfig_sfp_id_is_cmis(ii.id)) {
+ /* Lower memory (0-127), Page 00h (128-255), Page 11h */
+ read_i2c(&ii, CMIS_BASE, 0, 128, buf);
+ read_i2c_page(&ii, CMIS_BASE, 0x00, 0, 128, 128,
+ buf + 128);
+ read_i2c_page(&ii, CMIS_BASE, 0x11, 0, 128, 128,
+ buf + CMIS_DUMP_P11);
+ } else if (ifconfig_sfp_id_is_qsfp(ii.id)) {
read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP0_START, QSFP_DUMP0_SIZE,
buf + QSFP_DUMP0_START);
read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP1_START, QSFP_DUMP1_SIZE,
@@ -580,6 +803,9 @@ ifconfig_sfp_dump_region_count(const struct ifconfig_sfp_dump *dp)
{
uint8_t id_byte = dp->data[0];
+ if (ifconfig_sfp_id_is_cmis((enum sfp_id)id_byte))
+ return (3);
+
switch ((enum sfp_id)id_byte) {
case SFP_ID_UNKNOWN:
return (0);
diff --git a/lib/libifconfig/libifconfig_sfp.h b/lib/libifconfig/libifconfig_sfp.h
index 2e5374d6729e..9ed4f684e5c4 100644
--- a/lib/libifconfig/libifconfig_sfp.h
+++ b/lib/libifconfig/libifconfig_sfp.h
@@ -83,11 +83,15 @@ struct ifconfig_sfp_status {
#define QSFP_DUMP1_SIZE 128 /**< bytes in the second region
in a QSFP module dump */
+#define CMIS_DUMP_SIZE 512 /**< CMIS dump buffer size */
+#define CMIS_DUMP_P11 256 /**< offset of Page 11h in dump buffer */
+
/** SFP module I2C memory dump
- * SFP modules have one region, QSFP modules have two regions.
+ * SFP modules have one region, QSFP modules have two.
+ * CMIS modules have three: lower memory, Page 00h, and Page 11h.
*/
struct ifconfig_sfp_dump {
- uint8_t data[SFF_DUMP_SIZE]; /**< memory dump data */
+ uint8_t data[CMIS_DUMP_SIZE]; /**< memory dump data */
};
/** Get information about the static properties of an SFP/QSFP module
@@ -126,6 +130,25 @@ ifconfig_sfp_id_is_qsfp(enum sfp_id id)
}
}
+/** Is the given module ID a CMIS-managed module (QSFP-DD, OSFP, etc.)
+ * @param id The sfp_id field of a SFP module info object
+ * @return A bool true if CMIS-type sfp_id otherwise false
+ */
+static inline bool
+ifconfig_sfp_id_is_cmis(enum sfp_id id)
+{
+ switch (id) {
+ case SFP_ID_QSFP_DD:
+ case SFP_ID_QSFP8X:
+ case SFP_ID_SFP_DD:
+ case SFP_ID_DSFP:
+ case SFP_ID_QSFP_CMIS:
+ return (true);
+ default:
+ return (false);
+ }
+}
+
/** Get string descriptions of the given SFP/QSFP module info
* The strings are static and do not need to be freed.
* @see ifconfig_sfp_get_sfp_info to obtain the input info.
diff --git a/lib/libifconfig/sfp.lua b/lib/libifconfig/sfp.lua
index 4a43b2ed780b..1da49e35cade 100644
--- a/lib/libifconfig/sfp.lua
+++ b/lib/libifconfig/sfp.lua
@@ -359,6 +359,126 @@ enums = {
{0x0, "UNSPECIFIED", "Unspecified"},
},
},
+
+ "CMIS (OIF-CMIS-05.3) Media Type Encodings (Table 8-20)",
+ {
+ name = "cmis_media_type",
+ description = "CMIS media type",
+ bits = 8,
+ values = {
+ {0x00, "UNDEFINED", "Undefined"},
+ {0x01, "MMF", "Optical: MMF"},
+ {0x02, "SMF", "Optical: SMF"},
+ {0x03, "COPPER", "Passive/Linear Active Copper"},
+ {0x04, "ACTIVE", "Active Cable"},
+ {0x05, "BASET", "BASE-T"},
+ },
+ },
+
+ "CMIS Media Lane Count (from Application Descriptor)",
+ {
+ name = "cmis_lanes",
+ description = "CMIS media lane count",
+ bits = 8,
+ values = {
+ {0, "UNKNOWN", "Unknown"},
+ {1, "1", "1 lane"},
+ {2, "2", "2 lanes"},
+ {4, "4", "4 lanes"},
+ {8, "8", "8 lanes"},
+ },
+ },
+
+ "SFF-8024 Table 4-7: SMF Media Interface IDs (for CMIS MediaType=02h)",
+ "Verified against SFF-8024 Rev 4.6+ and SONiC sff8024.py",
+ {
+ name = "cmis_smf",
+ description = "CMIS SMF media interface",
+ bits = 8,
+ values = {
+ {0x00, "UNDEFINED", "Undefined"},
+ {0x01, "10GBASE_LW", "10GBASE-LW"},
+ {0x02, "10GBASE_EW", "10GBASE-EW"},
+ {0x03, "10G_ZW", "10G-ZW"},
+ {0x04, "10GBASE_LR", "10GBASE-LR"},
+ {0x05, "10GBASE_ER", "10GBASE-ER"},
+ {0x06, "10G_ZR", "10G-ZR"},
+ {0x07, "25GBASE_LR", "25GBASE-LR"},
+ {0x08, "25GBASE_ER", "25GBASE-ER"},
+ {0x09, "40GBASE_LR4", "40GBASE-LR4"},
+ {0x0A, "40GBASE_FR", "40GBASE-FR"},
+ {0x0B, "50GBASE_FR", "50GBASE-FR"},
+ {0x0C, "50GBASE_LR", "50GBASE-LR"},
+ {0x0D, "100GBASE_LR4", "100GBASE-LR4"},
+ {0x0E, "100GBASE_ER4", "100GBASE-ER4"},
+ {0x0F, "100G_PSM4", "100G PSM4"},
+ {0x10, "100G_CWDM4", "100G CWDM4"},
+ {0x11, "100G_4WDM_10", "100G 4WDM-10"},
+ {0x12, "100G_4WDM_20", "100G 4WDM-20"},
+ {0x13, "100G_4WDM_40", "100G 4WDM-40"},
+ {0x14, "100GBASE_DR", "100GBASE-DR"},
+ {0x15, "100G_FR", "100G-FR/100GBASE-FR1"},
+ {0x16, "100G_LR", "100G-LR/100GBASE-LR1"},
+ {0x17, "200GBASE_DR4", "200GBASE-DR4"},
+ {0x18, "200GBASE_FR4", "200GBASE-FR4"},
+ {0x19, "200GBASE_LR4", "200GBASE-LR4"},
+ {0x1A, "400GBASE_FR8", "400GBASE-FR8"},
+ {0x1B, "400GBASE_LR8", "400GBASE-LR8"},
+ {0x1C, "400GBASE_DR4", "400GBASE-DR4"},
+ {0x1D, "400G_FR4", "400G-FR4/400GBASE-FR4"},
+ {0x1E, "400G_LR4_10", "400G-LR4-10"},
+ {0x1F, "8GFC_SM", "8GFC-SM"},
+ {0x20, "10GFC_SM", "10GFC-SM"},
+ {0x21, "16GFC_SM", "16GFC-SM"},
+ {0x22, "32GFC_SM", "32GFC-SM"},
+ {0x23, "64GFC_SM", "64GFC-SM"},
+ {0x24, "128GFC_PSM4", "128GFC-PSM4"},
+ {0x25, "256GFC_PSM4", "256GFC-PSM4"},
+ {0x34, "100G_CWDM4_OCP", "100G CWDM4-OCP"},
+ {0x3E, "400ZR_DWDM", "400ZR DWDM"},
+ {0x40, "50GBASE_ER", "50GBASE-ER"},
+ {0x41, "200GBASE_ER4", "200GBASE-ER4"},
+ {0x42, "400GBASE_ER8", "400GBASE-ER8"},
+ {0x43, "400GBASE_LR4_6", "400GBASE-LR4-6"},
+ },
+ },
+
+ "SFF-8024 Table 4-6: MMF Media Interface IDs (for CMIS MediaType=01h)",
+ "Verified against SFF-8024 Rev 4.6+ and SONiC sff8024.py",
+ {
+ name = "cmis_mmf",
+ description = "CMIS MMF media interface",
+ bits = 8,
+ values = {
+ {0x00, "UNDEFINED", "Undefined"},
+ {0x01, "10GBASE_SW", "10GBASE-SW"},
+ {0x02, "10GBASE_SR", "10GBASE-SR"},
+ {0x03, "25GBASE_SR", "25GBASE-SR"},
+ {0x04, "40GBASE_SR4", "40GBASE-SR4"},
+ {0x05, "40GE_SWDM4", "40GE SWDM4"},
+ {0x06, "40GE_BIDI", "40GE BiDi"},
+ {0x07, "50GBASE_SR", "50GBASE-SR"},
+ {0x08, "100GBASE_SR10", "100GBASE-SR10"},
+ {0x09, "100GBASE_SR4", "100GBASE-SR4"},
+ {0x0A, "100GE_SWDM4", "100GE SWDM4"},
+ {0x0B, "100GE_BIDI", "100GE BiDi"},
+ {0x0C, "100GBASE_SR2", "100GBASE-SR2"},
+ {0x0D, "100G_SR", "100G-SR"},
+ {0x0E, "200GBASE_SR4", "200GBASE-SR4"},
+ {0x0F, "400GBASE_SR16", "400GBASE-SR16"},
+ {0x10, "400GBASE_SR8", "400GBASE-SR8"},
+ {0x11, "400G_SR4", "400G-SR4"},
+ {0x12, "800G_SR8", "800G-SR8"},
+ {0x13, "8GFC_MM", "8GFC-MM"},
+ {0x14, "10GFC_MM", "10GFC-MM"},
+ {0x15, "16GFC_MM", "16GFC-MM"},
+ {0x16, "32GFC_MM", "32GFC-MM"},
+ {0x17, "64GFC_MM", "64GFC-MM"},
+ {0x18, "128GFC_MM4", "128GFC-MM4"},
+ {0x19, "256GFC_MM4", "256GFC-MM4"},
+ {0x1A, "400GBASE_SR4_2", "400GBASE-SR4.2"},
+ },
+ },
}
-- Nothing else in this context.
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index cd484949e4da..59783592a370 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -4084,7 +4084,7 @@ pfctl_state_limiter_add(struct pfctl_handle *h, struct pfctl_state_lim *lim)
return (ENXIO);
while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
- if (! snl_parse_nlmsg(&h->ss, hdr, &statelim_parser, &lim))
+ if (! snl_parse_nlmsg(&h->ss, hdr, &statelim_parser, lim))
continue;
}
@@ -4153,7 +4153,7 @@ pfctl_source_limiter_add(struct pfctl_handle *h, struct pfctl_source_lim *lim)
return (ENXIO);
while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
- if (! snl_parse_nlmsg(&h->ss, hdr, &sourcelim_parser, &lim))
+ if (! snl_parse_nlmsg(&h->ss, hdr, &sourcelim_parser, lim))
continue;
}
diff --git a/lib/libpmc/libpmc_json.cc b/lib/libpmc/libpmc_json.cc
index 90881f22971e..1ac0b3a50a6c 100644
--- a/lib/libpmc/libpmc_json.cc
+++ b/lib/libpmc/libpmc_json.cc
@@ -74,8 +74,8 @@ startentry(struct pmclog_ev *ev)
{
char eventbuf[128];
- snprintf(eventbuf, sizeof(eventbuf), "%s, \"tsc\": \"%jd\"",
- typenames[ev->pl_type], (uintmax_t)ev->pl_ts.tv_sec);
+ snprintf(eventbuf, sizeof(eventbuf), "%s, \"tsc\": \"%ju\"",
+ typenames[ev->pl_type], (uintmax_t)ev->pl_tsc);
return (string(eventbuf));
}
@@ -393,4 +393,3 @@ event_to_json(struct pmclog_ev *ev){
errx(EX_USAGE, "ERROR: unrecognized event type: %d\n", ev->pl_type);
}
}
-
diff --git a/lib/libpmc/pmclog.3 b/lib/libpmc/pmclog.3
index 4af4150d60a9..452a14f42f55 100644
--- a/lib/libpmc/pmclog.3
+++ b/lib/libpmc/pmclog.3
@@ -77,7 +77,10 @@ struct pmclog_ev {
enum pmclog_state pl_state; /* parser state after 'get_event()' */
off_t pl_offset; /* byte offset in stream */
size_t pl_count; /* count of records so far */
- struct timespec pl_ts; /* log entry timestamp */
+ union {
+ uint64_t pl_tsc; /* TSC timestamp */
+ struct timespec pl_ts; /* log entry timestamp (legacy) */
+ };
enum pmclog_type pl_type; /* log entry kind */
union { /* log entry data */
struct pmclog_ev_callchain pl_cc;
@@ -136,8 +139,30 @@ Field
.Va pl_count
contains the serial number of this event.
Field
+.Va pl_tsc
+carries the raw CPU Time Stamp Counter (TSC) value recorded at the time
+of the event.
+This is not a wall-clock time; to convert to nanoseconds divide the TSC
+delta between two events by the TSC frequency
+.Pq Va pl_u.pl_i.pl_tsc_freq
+reported in the
+.Dv PMCLOG_TYPE_INITIALIZE
+record.
+The legacy
.Va pl_ts
-contains a timestamp with the system time when the event occurred.
+member aliases the same storage for ABI compatibility, but its contents
+no longer represent a wall-clock timestamp.
+.Pp
+Note that TSC-based timestamps and
+.Va pl_u.pl_i.pl_tsc_freq
+are only meaningful on x86 architectures
+.Pq amd64 and i386 .
+On all other architectures
+.Pq including arm64 and powerpc ,
+.Va pl_tsc
+and
+.Va pl_u.pl_i.pl_tsc_freq
+are set to zero.
Field
.Va pl_type
denotes the kind of the event returned in argument
diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c
index 22ff50b10ee0..c3587af46c7b 100644
--- a/lib/libpmc/pmclog.c
+++ b/lib/libpmc/pmclog.c
@@ -287,8 +287,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
return -1;
}
- /* copy out the time stamp */
- ev->pl_ts.tv_sec = ph->pl_tsc;
+ /* copy out the TSC from the event header */
+ ev->pl_tsc = ph->pl_tsc;
le += sizeof(*ph)/4;
evlen = PMCLOG_HEADER_TO_LENGTH(h);
diff --git a/lib/libpmc/pmclog.h b/lib/libpmc/pmclog.h
index 72e73e3928d2..a79d33529890 100644
--- a/lib/libpmc/pmclog.h
+++ b/lib/libpmc/pmclog.h
@@ -169,7 +169,10 @@ struct pmclog_ev {
enum pmclog_state pl_state; /* state after 'get_event()' */
off_t pl_offset; /* byte offset in stream */
size_t pl_count; /* count of records so far */
- struct timespec pl_ts; /* log entry timestamp */
+ union {
+ uint64_t pl_tsc; /* TSC timestamp */
+ struct timespec pl_ts; /* log entry timestamp (legacy) */
+ };
enum pmclog_type pl_type; /* type of log entry */
void *pl_data;
int pl_len;
@@ -229,4 +232,3 @@ void pmclog_close(void *_cookie);
__END_DECLS
#endif
-
diff --git a/lib/libsecureboot/h/verify_file.h b/lib/libsecureboot/h/verify_file.h
index f918ed6d0e38..19bef24b1dee 100644
--- a/lib/libsecureboot/h/verify_file.h
+++ b/lib/libsecureboot/h/verify_file.h
@@ -51,6 +51,7 @@ int ve_status_get(int);
int load_manifest(const char *, const char *, const char *, struct stat *);
int pass_manifest(const char *, const char *);
int pass_manifest_export_envs(void);
+int severity_guess(const char *);
void verify_report(const char *, int, int, struct stat *);
int verify_file(int, const char *, off_t, int, const char *);
void verify_pcr_export(void);
@@ -59,9 +60,9 @@ int is_verified(struct stat *);
void add_verify_status(struct stat *, int);
struct vectx;
-struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *, const char *);
+struct vectx* vectx_open(int, const char *, int, off_t, struct stat *, int *, const char *);
ssize_t vectx_read(struct vectx *, void *, size_t);
off_t vectx_lseek(struct vectx *, off_t, int);
-int vectx_close(struct vectx *, int, const char *);
+int vectx_close(struct vectx *, const char *);
#endif /* _VERIFY_FILE_H_ */
diff --git a/lib/libsecureboot/tests/tvo.c b/lib/libsecureboot/tests/tvo.c
index 7851e79c5a2a..407fcbefd6db 100644
--- a/lib/libsecureboot/tests/tvo.c
+++ b/lib/libsecureboot/tests/tvo.c
@@ -170,8 +170,8 @@ main(int argc, char *argv[])
fstat(fd, &st);
lseek(fd, 0, SEEK_SET);
off = st.st_size % 512;
- vp = vectx_open(fd, argv[optind], off,
- &st, &error, __func__);
+ vp = vectx_open(fd, argv[optind], VE_GUESS,
+ off, &st, &error, __func__);
if (!vp) {
printf("vectx_open(%s) failed: %d %s\n",
argv[optind], error,
@@ -190,7 +190,7 @@ main(int argc, char *argv[])
off = vectx_lseek(vp, 0, SEEK_END);
/* repeating that should be harmless */
off = vectx_lseek(vp, 0, SEEK_END);
- error = vectx_close(vp, VE_MUST, __func__);
+ error = vectx_close(vp, __func__);
if (error) {
printf("vectx_close(%s) == %d %s\n",
argv[optind], error,
diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c
index 2d56830cd81d..4b42293a0d93 100644
--- a/lib/libsecureboot/vectx.c
+++ b/lib/libsecureboot/vectx.c
@@ -60,6 +60,7 @@ struct vectx {
int vec_fd; /* file descriptor */
int vec_status; /* verification status */
int vec_closing; /* we are closing */
+ int vec_severity; /* usually VE_MUST */
};
@@ -93,7 +94,8 @@ struct vectx {
* NULL is only returned for non-files or out-of-memory.
*/
struct vectx *
-vectx_open(int fd, const char *path, off_t off, struct stat *stp,
+vectx_open(int fd, const char *path, int severity,
+ off_t off, struct stat *stp,
int *error, const char *caller)
{
struct vectx *ctx;
@@ -106,14 +108,19 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp,
stp = &st;
rc = verify_prep(fd, path, off, stp, __func__);
+ if (severity == VE_GUESS)
+ severity = severity_guess(path);
DEBUG_PRINTF(2,
- ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d\n",
- caller, fd, path, rc));
+ ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d,severity=%d\n",
+ caller, fd, path, rc, severity));
switch (rc) {
case VE_FINGERPRINT_NONE:
case VE_FINGERPRINT_UNKNOWN:
+ if (severity < VE_MUST)
+ break;
+ /* FALLTHROUGH */
case VE_FINGERPRINT_WRONG:
*error = rc;
return (NULL);
@@ -127,19 +134,24 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp,
ctx->vec_off = 0;
ctx->vec_hashed = 0;
ctx->vec_want = NULL;
+ ctx->vec_severity = severity;
ctx->vec_status = 0;
ctx->vec_hashsz = hashsz = 0;
ctx->vec_closing = 0;
- if (rc == 0) {
+ if (rc == VE_UNVERIFIED_OK) {
/* we are not verifying this */
*error = 0;
return (ctx);
}
cp = fingerprint_info_lookup(fd, path);
if (!cp) {
- ctx->vec_status = VE_FINGERPRINT_NONE;
- ve_error_set("%s: no entry", path);
+ if (severity < VE_MUST)
+ ctx->vec_status = VE_UNVERIFIED_OK;
+ else {
+ ctx->vec_status = VE_FINGERPRINT_NONE;
+ ve_error_set("%s: no entry", path);
+ }
} else {
if (strncmp(cp, "no_hash", 7) == 0) {
ctx->vec_status = VE_FINGERPRINT_IGNORE;
@@ -167,8 +179,12 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp,
cp += 7;
#endif
} else {
- ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
- ve_error_set("%s: no supported fingerprint", path);
+ if (severity < VE_MUST)
+ ctx->vec_status = VE_UNVERIFIED_OK;
+ else {
+ ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
+ ve_error_set("%s: no supported fingerprint", path);
+ }
}
}
*error = ctx->vec_status;
@@ -183,9 +199,9 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp,
}
}
DEBUG_PRINTF(2,
- ("vectx_open: caller=%s,name='%s',hashsz=%lu,status=%d\n",
+ ("vectx_open: caller=%s,name='%s',hashsz=%lu,severity=%d,status=%d\n",
caller, path, (unsigned long)ctx->vec_hashsz,
- ctx->vec_status));
+ severity, ctx->vec_status));
return (ctx);
enomem: /* unlikely */
@@ -379,7 +395,7 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence)
* @return 0 or an error.
*/
int
-vectx_close(struct vectx *ctx, int severity, const char *caller)
+vectx_close(struct vectx *ctx, const char *caller)
{
int rc;
@@ -393,7 +409,7 @@ vectx_close(struct vectx *ctx, int severity, const char *caller)
* these tend to be processed in a more deterministic
* order, which makes our pseudo pcr more useful.
*/
- ve_pcr_updating_set((severity == VE_MUST));
+ ve_pcr_updating_set((ctx->vec_severity == VE_MUST));
#endif
/* make sure we have hashed it all */
vectx_lseek(ctx, 0, SEEK_END);
@@ -401,13 +417,13 @@ vectx_close(struct vectx *ctx, int severity, const char *caller)
ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
}
DEBUG_PRINTF(2,
- ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
- caller,ctx->vec_path, rc, severity));
- verify_report(ctx->vec_path, severity, rc, NULL);
+ ("vectx_close: caller=%s,name='%s',severity=%d,rc=%d\n",
+ caller,ctx->vec_path, ctx->vec_severity, rc));
+ verify_report(ctx->vec_path, ctx->vec_severity, rc, NULL);
if (rc == VE_FINGERPRINT_WRONG) {
#if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
/* we are generally called with VE_MUST */
- if (severity > VE_WANT)
+ if (ctx->vec_severity > VE_WANT)
panic("cannot continue");
#endif
}
diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c
index ee263dafe774..b1aad36672d0 100644
--- a/lib/libsecureboot/verify_file.c
+++ b/lib/libsecureboot/verify_file.c
@@ -271,7 +271,7 @@ find_manifest(const char *name)
# define ACCEPT_NO_FP_DEFAULT VE_MUST
#endif
-static int
+int
severity_guess(const char *filename)
{
const char *cp;
@@ -285,6 +285,7 @@ severity_guess(const char *filename)
*/
if ((cp = strrchr(filename, '.'))) {
if (strcmp(cp, ".cookie") == 0 ||
+ strcmp(cp, ".dof") == 0 ||
strcmp(cp, ".hints") == 0 ||
strcmp(cp, ".order") == 0 ||
strcmp(cp, ".tgz") == 0)
diff --git a/lib/libsys/fork.2 b/lib/libsys/fork.2
index e59b208a9ff5..89a5631d7daa 100644
--- a/lib/libsys/fork.2
+++ b/lib/libsys/fork.2
@@ -138,6 +138,7 @@ services (
or
.Xr rtld 1 )
are available in the child if forked from multi-threaded parent.
+.Pp
In particular, if using dynamic linking, all dynamic symbols used by the
child after
.Fn _Fork
@@ -151,6 +152,7 @@ option to the static linker
or by using each symbol before the
.Fn _Fork
call to force the binding.
+Either of the methods subtly changes the ABI of the resulting binary.
.Sh RETURN VALUES
Upon successful completion,
.Fn fork
diff --git a/lib/libsys/ntp_adjtime.2 b/lib/libsys/ntp_adjtime.2
index 5be5194a9c1d..ed43fb79a9f1 100644
--- a/lib/libsys/ntp_adjtime.2
+++ b/lib/libsys/ntp_adjtime.2
@@ -27,13 +27,13 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 13, 2005
+.Dd March 22, 2026
.Dt NTP_ADJTIME 2
.Os
.Sh NAME
.Nm ntp_adjtime ,
.Nm ntp_gettime
-.Nd Network Time Protocol (NTP) daemon interface system calls
+.Nd Network Time Protocol daemon (ntpd) interface system calls
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -59,7 +59,7 @@ The time offset and related variables which are set by
are used by
.Fn hardclock
to adjust the phase and frequency of the phase- or frequency-lock loop
-(PLL resp. FLL) which controls the system clock.
+(PLL resp\&. FLL) which controls the system clock.
.Pp
The
.Fn ntp_gettime
diff --git a/lib/libsys/x86/pkru.3 b/lib/libsys/x86/pkru.3
index 95bc66c979ac..75580953e6ed 100644
--- a/lib/libsys/x86/pkru.3
+++ b/lib/libsys/x86/pkru.3
@@ -71,17 +71,17 @@ Only one key may apply to a given range at a time.
The default protection key index is zero, it is used even if no key
was explicitly assigned to the address, or if the key was removed.
.Pp
-The protection prevents the system from accessing user addresses as well
-as the user applications.
-When a system call was unable to read or write user memory due to key
-protection, it returns the
-.Er EFAULT
-error code.
-Note that some side effects may have occurred if this error is reported.
+If the user application attempts a memory access which is prohibited by the
+PKRU register, the offending thread receives a synchronous
+.Dv SIGSEGV
+signal with
+.Va si_code
+set to
+.Dv SEGV_PKUERR .
+PKRU protections might prevent the kernel from accessing protected
+user addresses when handling system calls, but this is not guaranteed and
+must not be relied upon.
.Pp
-Protection keys require that the system uses 4-level paging
-(also called long mode),
-which means that it is only available on amd64 system.
Both 64-bit and 32-bit applications can use protection keys.
More information about the hardware feature is provided in the IA32 Software
Developer's Manual published by Intel Corp.
diff --git a/lib/libthr/libthr.3 b/lib/libthr/libthr.3
index b84176abcd32..5d9c5ec6706b 100644
--- a/lib/libthr/libthr.3
+++ b/lib/libthr/libthr.3
@@ -52,6 +52,12 @@ The library is tightly integrated with the run-time link editor
and
.Lb libc ;
all three components must be built from the same source tree.
+Together, they constitute the base C runtime environment of
+.Fx ,
+running on top of the
+.Fx
+kernel.
+.Pp
Mixing
.Li libc
and
@@ -263,6 +269,25 @@ the critical section.
This should be taken into account when interpreting
.Xr ktrace 1
logs.
+.Pp
+The
+.Nm
+library uses the
+.Va SIGTHR
+signal for internal operations, in particular,
+for cancellation requests.
+This signal's masking and disposition is controlled by the library,
+and user programs should not try to modify them.
+The library interposes functions controlling signals to prevent
+inadvertent modifications and to guard portable code against
+exposure to
+.Va SIGTHR .
+.Pp
+Note: similarly, the
+.Va SIGLIBRT
+signal is reserved for use by
+.Lb librt ,
+and should be not modified by users.
.Sh PROCESS-SHARED SYNCHRONIZATION OBJECTS
In the
.Li libthr
diff --git a/lib/msun/Makefile b/lib/msun/Makefile
index 4a0ff1ad92b6..d61f4e9a1659 100644
--- a/lib/msun/Makefile
+++ b/lib/msun/Makefile
@@ -76,7 +76,9 @@ COMMON_SRCS= b_tgamma.c \
s_finite.c s_finitef.c \
s_floor.c s_floorf.c s_fma.c s_fmaf.c \
s_fmax.c s_fmaxf.c s_fmaximum.c s_fmaximumf.c \
+ s_fmaximum_mag.c s_fmaximum_magf.c s_fmaximum_num.c s_fmaximum_numf.c \
s_fmin.c s_fminf.c s_fminimum.c s_fminimumf.c \
+ s_fminimum_mag.c s_fminimum_magf.c s_fminimum_num.c s_fminimum_numf.c \
s_frexp.c s_frexpf.c s_ilogb.c s_ilogbf.c \
s_ilogbl.c s_isfinite.c s_isnan.c s_isnormal.c \
s_llrint.c s_llrintf.c s_llround.c s_llroundf.c s_llroundl.c \
@@ -133,7 +135,8 @@ COMMON_SRCS+= b_tgammal.c catrigl.c \
s_asinhl.c s_atanl.c s_cbrtl.c s_ceill.c s_cexpl.c \
s_clogl.c s_cosl.c s_cospil.c s_cprojl.c \
s_csqrtl.c s_erfl.c s_exp2l.c s_expl.c s_floorl.c s_fmal.c \
- s_fmaxl.c s_fmaximuml.c s_fminl.c s_fminimuml.c \
+ s_fmaxl.c s_fmaximuml.c s_fmaximum_magl.c s_fmaximum_numl.c \
+ s_fminl.c s_fminimuml.c s_fminimum_magl.c s_fminimum_numl.c \
s_frexpl.c s_logbl.c s_logl.c s_nanl.c \
s_nextafterl.c s_nexttoward.c s_remquol.c s_rintl.c s_roundl.c \
s_scalbnl.c s_sinl.c s_sincosl.c s_sinpil.c \
@@ -179,7 +182,8 @@ MAN= acos.3 acosh.3 asin.3 asinh.3 atan.3 atan2.3 atanh.3 \
exp.3 fabs.3 fdim.3 \
feclearexcept.3 feenableexcept.3 fegetenv.3 \
fegetround.3 fenv.3 floor.3 fma.3 \
- fmax.3 fmaximum.3 fmod.3 hypot.3 ieee.3 ieee_test.3 ilogb.3 j0.3 \
+ fmax.3 fmaximum.3 fmaximum_mag.3 fmaximum_num.3 fmod.3 \
+ hypot.3 ieee.3 ieee_test.3 ilogb.3 j0.3 \
lgamma.3 log.3 lrint.3 lround.3 math.3 nan.3 \
nextafter.3 remainder.3 rint.3 \
round.3 scalbn.3 signbit.3 sin.3 sincos.3 \
@@ -232,9 +236,15 @@ MLINKS+=floor.3 floorf.3 floor.3 floorl.3
MLINKS+=fma.3 fmaf.3 fma.3 fmal.3
MLINKS+=fmax.3 fmaxf.3 fmax.3 fmaxl.3 \
fmax.3 fmin.3 fmax.3 fminf.3 fmax.3 fminl.3
-MLINKS+=fmaximum.3 fmaximuml.3 fmaximum.3 fmaximumf.3 \
+MLINKS+=fmaximum.3 fmaximumf.3 fmaximum.3 fmaximuml.3 \
fmaximum.3 fminimum.3 fmaximum.3 fminimumf.3 \
fmaximum.3 fminimuml.3
+MLINKS+=fmaximum_mag.3 fmaximum_magf.3 fmaximum_mag.3 fmaximum_magl.3 \
+ fmaximum_mag.3 fminimum_mag.3 fmaximum_mag.3 fminimum_magf.3 \
+ fmaximum_mag.3 fminimum_magl.3
+MLINKS+=fmaximum_num.3 fmaximum_numf.3 fmaximum_num.3 fmaximum_numl.3 \
+ fmaximum_num.3 fminimum_num.3 fmaximum_num.3 fminimum_numf.3 \
+ fmaximum_num.3 fminimum_numl.3
MLINKS+=fmod.3 fmodf.3 fmod.3 fmodl.3
MLINKS+=hypot.3 cabs.3 hypot.3 cabsf.3 hypot.3 cabsl.3 \
hypot.3 hypotf.3 hypot.3 hypotl.3
diff --git a/lib/msun/Symbol.map b/lib/msun/Symbol.map
index 932cc000fe52..35addfcee3d5 100644
--- a/lib/msun/Symbol.map
+++ b/lib/msun/Symbol.map
@@ -326,4 +326,16 @@ FBSD_1.9 {
fminimum;
fminimumf;
fminimuml;
+ fmaximum_mag;
+ fmaximum_magf;
+ fmaximum_magl;
+ fminimum_mag;
+ fminimum_magf;
+ fminimum_magl;
+ fmaximum_num;
+ fmaximum_numf;
+ fmaximum_numl;
+ fminimum_num;
+ fminimum_numf;
+ fminimum_numl;
};
diff --git a/lib/msun/man/fmax.3 b/lib/msun/man/fmax.3
index 873a19375ce5..3b167a0b6f8d 100644
--- a/lib/msun/man/fmax.3
+++ b/lib/msun/man/fmax.3
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 6, 2026
+.Dd April 11, 2026
.Dt FMAX 3
.Os
.Sh NAME
@@ -93,6 +93,13 @@ as being larger than
.Li -0.0 .
This behavior is not specified by the C standard, is not portable,
and may not occur in light of compiler optimizations.
+Applications requiring specific handling of signed zeroes or
+.No \*(Na
+values are recommended to use
+.Xr fmaximum_num 3
+and
+.Xr fminimum_num 3
+instead, which have strictly defined behavior for these cases.
.Sh HISTORY
These routines first appeared in
.Fx 5.3 .
diff --git a/lib/msun/man/fmaximum.3 b/lib/msun/man/fmaximum.3
index bd301163a034..ef26c12268c0 100644
--- a/lib/msun/man/fmaximum.3
+++ b/lib/msun/man/fmaximum.3
@@ -83,9 +83,9 @@ or
is an \*(Na, then the result is an \*(Na.
These routines do not raise any floating-point exceptions.
.Sh SEE ALSO
-.Xr fabs 3 ,
-.Xr fdim 3 ,
.Xr fmax 3 ,
+.Xr fmaximum_num 3 ,
+.Xr fmaximum_mag 3 ,
.Xr math 3
.Sh STANDARDS
The
diff --git a/lib/msun/man/fmaximum_mag.3 b/lib/msun/man/fmaximum_mag.3
new file mode 100644
index 000000000000..f5e4c39f96ef
--- /dev/null
+++ b/lib/msun/man/fmaximum_mag.3
@@ -0,0 +1,102 @@
+.\" Copyright (c) 2004 David Schultz <das@FreeBSD.org>
+.\" Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 3, 2026
+.Dt FMAXIMUM_MAG 3
+.Os
+.Sh NAME
+.Nm fmaximum_mag ,
+.Nm fmaximum_magf ,
+.Nm fmaximum_magl ,
+.Nm fminimum_mag ,
+.Nm fminimum_magf ,
+.Nm fminimum_magl
+.Nd floating-point maximum and minimum magnitude functions
+.Sh LIBRARY
+.Lb libm
+.Sh SYNOPSIS
+.In math.h
+.Ft double
+.Fn fmaximum_mag "double x" "double y"
+.Ft float
+.Fn fmaximum_magf "float x" "float y"
+.Ft "long double"
+.Fn fmaximum_magl "long double x" "long double y"
+.Ft double
+.Fn fminimum_mag "double x" "double y"
+.Ft float
+.Fn fminimum_magf "float x" "float y"
+.Ft "long double"
+.Fn fminimum_magl "long double x" "long double y"
+.Sh DESCRIPTION
+The
+.Fn fmaximum_mag ,
+.Fn fmaximum_magf ,
+and
+.Fn fmaximum_magl
+functions determine the larger of the absolute values of
+.Fa x
+and
+.Fa y ,
+and return the argument with the larger absolute value.
+If the absolute values are equal, the behavior is equivalent to calling the corresponding
+.Fn fmaximum
+function on the arguments.
+.Pp
+Likewise, the
+.Fn fminimum_mag ,
+.Fn fminimum_magf ,
+and
+.Fn fminimum_magl
+functions determine the smaller of the absolute values of
+.Fa x
+and
+.Fa y ,
+and return the argument with the smaller absolute value.
+If the absolute values are equal, the behavior is equivalent to calling the corresponding
+.Fn fminimum
+function on the arguments.
+.Pp
+If either argument is an \*(Na, then the result is an \*(Na.
+These routines do not raise any floating-point exceptions.
+.Sh SEE ALSO
+.Xr fmax 3 ,
+.Xr fmaximum 3 ,
+.Xr fmaximum_num 3 ,
+.Xr math 3
+.Sh STANDARDS
+The
+.Fn fmaximum_mag ,
+.Fn fmaximum_magf ,
+.Fn fmaximum_magl ,
+.Fn fminimum_mag ,
+.Fn fminimum_magf ,
+and
+.Fn fminimum_magl
+functions conform to
+.St -isoC-2023 .
+.Sh HISTORY
+These routines first appeared in
+.Fx 16.0 .
diff --git a/lib/msun/man/fmaximum_num.3 b/lib/msun/man/fmaximum_num.3
new file mode 100644
index 000000000000..33fa759f0173
--- /dev/null
+++ b/lib/msun/man/fmaximum_num.3
@@ -0,0 +1,113 @@
+.\" Copyright (c) 2004 David Schultz <das@FreeBSD.org>
+.\" Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 3, 2026
+.Dt FMAXIMUM_NUM 3
+.Os
+.Sh NAME
+.Nm fmaximum_num ,
+.Nm fmaximum_numf ,
+.Nm fmaximum_numl ,
+.Nm fminimum_num ,
+.Nm fminimum_numf ,
+.Nm fminimum_numl
+.Nd floating-point maximum and minimum number functions
+.Sh LIBRARY
+.Lb libm
+.Sh SYNOPSIS
+.In math.h
+.Ft double
+.Fn fmaximum_num "double x" "double y"
+.Ft float
+.Fn fmaximum_numf "float x" "float y"
+.Ft "long double"
+.Fn fmaximum_numl "long double x" "long double y"
+.Ft double
+.Fn fminimum_num "double x" "double y"
+.Ft float
+.Fn fminimum_numf "float x" "float y"
+.Ft "long double"
+.Fn fminimum_numl "long double x" "long double y"
+.Sh DESCRIPTION
+The
+.Fn fmaximum_num ,
+.Fn fmaximum_numf ,
+and
+.Fn fmaximum_numl
+functions determine the larger of
+.Fa x
+and
+.Fa y ,
+preferring a numeric value over an \*(Na.
+If one argument is a numeric value and the other is an \*(Na,
+the numeric value is returned.
+If both arguments are numeric, the larger value is returned.
+If both arguments are \*(Nas, a quiet \*(Na is returned.
+For the purpose of these functions, positive zero is considered
+greater than negative zero.
+.Pp
+Likewise, the
+.Fn fminimum_num ,
+.Fn fminimum_numf ,
+and
+.Fn fminimum_numl
+functions determine the smaller of
+.Fa x
+and
+.Fa y ,
+preferring a numeric value over an \*(Na.
+If one argument is a numeric value and the other is an \*(Na,
+the numeric value is returned.
+If both arguments are numeric, the smaller value is returned.
+If both arguments are \*(Nas, a quiet \*(Na is returned.
+For the purpose of these functions, negative zero is considered
+less than positive zero.
+.Pp
+Unlike with the
+.Xr fmaximum 3
+and
+.Xr fmaximum_mag 3
+families of functions, if either argument is a signaling \*(Na,
+an invalid exception is raised.
+Otherwise, these routines do not raise any floating-point exceptions.
+.Sh SEE ALSO
+.Xr fmax 3 ,
+.Xr fmaximum 3 ,
+.Xr fmaximum_mag 3 ,
+.Xr math 3
+.Sh STANDARDS
+The
+.Fn fmaximum_num ,
+.Fn fmaximum_numf ,
+.Fn fmaximum_numl ,
+.Fn fminimum_num ,
+.Fn fminimum_numf ,
+and
+.Fn fminimum_numl
+functions conform to
+.St -isoC-2023 .
+.Sh HISTORY
+These routines first appeared in
+.Fx 16.0 .
diff --git a/lib/msun/man/math.3 b/lib/msun/man/math.3
index 47353298bb54..f98c5e1a2a75 100644
--- a/lib/msun/man/math.3
+++ b/lib/msun/man/math.3
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 7, 2017
+.Dd April 11, 2026
.Dt MATH 3
.Os
.Sh NAME
@@ -115,8 +115,14 @@ scalbn adjust exponent
copysign copy sign bit
fabs absolute value
fdim positive difference
-fmax maximum function
-fmin minimum function
+fmax maximum function (legacy)
+fmaximum maximum function (prefers \*(Na)
+fmaximum_mag maximum magnitude (prefers \*(Na)
+fmaximum_num maximum function (avoids \*(Na)
+fmin minimum function (legacy)
+fminimum minimum function (prefers \*(Na)
+fminimum_mag minimum magnitude (prefers \*(Na)
+fminimum_num minimum function (avoids \*(Na)
signbit extract sign bit
.El
.Ss Not a Number Functions
diff --git a/lib/msun/src/math.h b/lib/msun/src/math.h
index 103b82c1cdf8..853984953a91 100644
--- a/lib/msun/src/math.h
+++ b/lib/msun/src/math.h
@@ -526,6 +526,18 @@ long double fmaximuml(long double, long double);
double fminimum(double, double);
float fminimumf(float, float);
long double fminimuml(long double, long double);
+double fmaximum_mag(double, double);
+float fmaximum_magf(float, float);
+long double fmaximum_magl(long double, long double);
+double fminimum_mag(double, double);
+float fminimum_magf(float, float);
+long double fminimum_magl(long double, long double);
+double fmaximum_num(double, double);
+float fmaximum_numf(float, float);
+long double fmaximum_numl(long double, long double);
+double fminimum_num(double, double);
+float fminimum_numf(float, float);
+long double fminimum_numl(long double, long double);
#endif /* __ISO_C_VISIBLE >= 2023 */
__END_DECLS
diff --git a/lib/msun/src/s_fmaximum_mag.c b/lib/msun/src/s_fmaximum_mag.c
new file mode 100644
index 000000000000..1b1250f4c36e
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_mag.c
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <float.h>
+#include <math.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMAXIMUM_MAG
+double
+fmaximum_mag(double x, double y)
+{
+ return (__builtin_fmaximum_mag(x, y));
+}
+#else
+double
+fmaximum_mag(double x, double y)
+{
+ union IEEEd2bits u[2];
+
+ u[0].d = x;
+ u[1].d = y;
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if ((u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0) ||
+ (u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0))
+ return (NAN);
+
+ double ax = fabs(x);
+ double ay = fabs(y);
+
+ if (ay > ax)
+ return (y);
+ if (ax > ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[0].bits.sign].d);
+
+ return (x);
+}
+#endif
+
+#if (LDBL_MANT_DIG == 53)
+__weak_reference(fmaximum_mag, fmaximum_magl);
+#endif
+
diff --git a/lib/msun/src/s_fmaximum_magf.c b/lib/msun/src/s_fmaximum_magf.c
new file mode 100644
index 000000000000..6193b9184970
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_magf.c
@@ -0,0 +1,68 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMAXIMUM_MAGF
+float
+fmaximum_magf(float x, float y)
+{
+ return (__builtin_fmaximum_magf(x, y));
+}
+#else
+float
+fmaximum_magf(float x, float y)
+{
+ union IEEEf2bits u[2];
+
+ u[0].f = x;
+ u[1].f = y;
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if ((u[0].bits.exp == 255 && u[0].bits.man != 0) ||
+ (u[1].bits.exp == 255 && u[1].bits.man != 0))
+ return (NAN);
+
+ float ax = fabsf(x);
+ float ay = fabsf(y);
+
+ if (ay > ax)
+ return (y);
+ if (ax > ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[0].bits.sign].f);
+
+ return (x);
+}
+#endif
+
diff --git a/lib/msun/src/s_fmaximum_magl.c b/lib/msun/src/s_fmaximum_magl.c
new file mode 100644
index 000000000000..f2426b050d33
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_magl.c
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include "fpmath.h"
+
+long double
+fmaximum_magl(long double x, long double y)
+{
+ union IEEEl2bits u[2];
+
+ u[0].e = x;
+ mask_nbit_l(u[0]);
+ u[1].e = y;
+ mask_nbit_l(u[1]);
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if ((u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0) ||
+ (u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0))
+ return (NAN);
+
+ long double ax = fabsl(x);
+ long double ay = fabsl(y);
+
+ if (ay > ax)
+ return (y);
+ if (ax > ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[0].bits.sign ? y : x);
+
+ return (x);
+}
+
diff --git a/lib/msun/src/s_fmaximum_num.c b/lib/msun/src/s_fmaximum_num.c
new file mode 100644
index 000000000000..cf16c76f89b9
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_num.c
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMAXIMUM_NUM
+double
+fmaximum_num(double x, double y)
+{
+ return (__builtin_fmaximum_num(x, y));
+}
+#else
+double
+fmaximum_num(double x, double y)
+{
+ union IEEEd2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].d = x;
+ u[1].d = y;
+
+ nan_x = u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0;
+ nan_y = u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[0].bits.sign].d);
+
+ return (x > y ? x : y);
+}
+#endif
+
+#if (LDBL_MANT_DIG == 53)
+__weak_reference(fmaximum_num, fmaximum_numl);
+#endif
diff --git a/lib/msun/src/s_fmaximum_numf.c b/lib/msun/src/s_fmaximum_numf.c
new file mode 100644
index 000000000000..c30179e47f9e
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_numf.c
@@ -0,0 +1,70 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMAXIMUM_NUMF
+float
+fmaximum_numf(float x, float y)
+{
+ return (__builtin_fmaximum_numf(x, y));
+}
+#else
+float
+fmaximum_numf(float x, float y)
+{
+ union IEEEf2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].f = x;
+ u[1].f = y;
+
+ nan_x = u[0].bits.exp == 255 && u[0].bits.man != 0;
+ nan_y = u[1].bits.exp == 255 && u[1].bits.man != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[0].bits.sign].f);
+
+ return (x > y ? x : y);
+}
+#endif
+
diff --git a/lib/msun/src/s_fmaximum_numl.c b/lib/msun/src/s_fmaximum_numl.c
new file mode 100644
index 000000000000..2291d01ca4f4
--- /dev/null
+++ b/lib/msun/src/s_fmaximum_numl.c
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+long double
+fmaximum_numl(long double x, long double y)
+{
+ union IEEEl2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].e = x;
+ mask_nbit_l(u[0]);
+ u[1].e = y;
+ mask_nbit_l(u[1]);
+
+ nan_x = u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0;
+ nan_y = u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[0].bits.sign ? y : x);
+
+ return (x > y ? x : y);
+}
diff --git a/lib/msun/src/s_fminimum.c b/lib/msun/src/s_fminimum.c
index 64c81b560626..fa3fd17fe241 100644
--- a/lib/msun/src/s_fminimum.c
+++ b/lib/msun/src/s_fminimum.c
@@ -47,7 +47,7 @@ fminimum(double x, double y)
u[0].d = x;
u[1].d = y;
- /* Check for NaNs to avoid raising spurious exceptions. */
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
if (u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0 ||
u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0)
return (NAN);
diff --git a/lib/msun/src/s_fminimum_mag.c b/lib/msun/src/s_fminimum_mag.c
new file mode 100644
index 000000000000..cd21fb948a8e
--- /dev/null
+++ b/lib/msun/src/s_fminimum_mag.c
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <float.h>
+#include <math.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMINIMUM_MAG
+double
+fminimum_mag(double x, double y)
+{
+ return (__builtin_fminimum_mag(x, y));
+}
+#else
+double
+fminimum_mag(double x, double y)
+{
+ union IEEEd2bits u[2];
+
+ u[0].d = x;
+ u[1].d = y;
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if (u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0 ||
+ u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0)
+ return (NAN);
+
+ double ax = fabs(x);
+ double ay = fabs(y);
+
+ if (ay < ax)
+ return (y);
+ if (ax < ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[1].bits.sign].d);
+
+ return (x);
+}
+#endif
+
+#if (LDBL_MANT_DIG == 53)
+__weak_reference(fminimum_mag, fminimum_magl);
+#endif
+
+
diff --git a/lib/msun/src/s_fminimum_magf.c b/lib/msun/src/s_fminimum_magf.c
new file mode 100644
index 000000000000..9c04859184ea
--- /dev/null
+++ b/lib/msun/src/s_fminimum_magf.c
@@ -0,0 +1,69 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMINIMUM_MAGF
+float
+fminimum_magf(float x, float y)
+{
+ return (__builtin_fminimum_magf(x, y));
+}
+#else
+float
+fminimum_magf(float x, float y)
+{
+ union IEEEf2bits u[2];
+
+ u[0].f = x;
+ u[1].f = y;
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if (u[0].bits.exp == 255 && u[0].bits.man != 0 ||
+ u[1].bits.exp == 255 && u[1].bits.man != 0)
+ return (NAN);
+
+ float ax = fabsf(x);
+ float ay = fabsf(y);
+
+ if (ay < ax)
+ return (y);
+ if (ax < ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[1].bits.sign].f);
+
+ return (x);
+}
+#endif
+
+
diff --git a/lib/msun/src/s_fminimum_magl.c b/lib/msun/src/s_fminimum_magl.c
new file mode 100644
index 000000000000..e6ab22afe7f0
--- /dev/null
+++ b/lib/msun/src/s_fminimum_magl.c
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include "fpmath.h"
+
+long double
+fminimum_magl(long double x, long double y)
+{
+ union IEEEl2bits u[2];
+
+ u[0].e = x;
+ mask_nbit_l(u[0]);
+ u[1].e = y;
+ mask_nbit_l(u[1]);
+
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
+ if (u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0 ||
+ u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0)
+ return (NAN);
+
+ long double ax = fabsl(x);
+ long double ay = fabsl(y);
+
+ if (ay < ax)
+ return (y);
+ if (ax < ay)
+ return (x);
+
+ /* If magnitudes are equal, we break the tie with the sign */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[1].bits.sign ? y : x);
+
+ return (x);
+}
+
+
diff --git a/lib/msun/src/s_fminimum_num.c b/lib/msun/src/s_fminimum_num.c
new file mode 100644
index 000000000000..71b5f072c32d
--- /dev/null
+++ b/lib/msun/src/s_fminimum_num.c
@@ -0,0 +1,76 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMINIMUM_NUM
+double
+fminimum_num(double x, double y)
+{
+ return (__builtin_fminimum_num(x, y));
+}
+#else
+double
+fminimum_num(double x, double y)
+{
+ union IEEEd2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].d = x;
+ u[1].d = y;
+
+ nan_x = u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0;
+ nan_y = u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[1].bits.sign].d);
+
+ return (x < y ? x : y);
+}
+#endif
+
+#if (LDBL_MANT_DIG == 53)
+__weak_reference(fminimum_num, fminimum_numl);
+#endif
+
+
diff --git a/lib/msun/src/s_fminimum_numf.c b/lib/msun/src/s_fminimum_numf.c
new file mode 100644
index 000000000000..d5bab31ce403
--- /dev/null
+++ b/lib/msun/src/s_fminimum_numf.c
@@ -0,0 +1,71 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+#ifdef USE_BUILTIN_FMINIMUM_NUMF
+float
+fminimum_numf(float x, float y)
+{
+ return (__builtin_fminimum_numf(x, y));
+}
+#else
+float
+fminimum_numf(float x, float y)
+{
+ union IEEEf2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].f = x;
+ u[1].f = y;
+
+ nan_x = u[0].bits.exp == 255 && u[0].bits.man != 0;
+ nan_y = u[1].bits.exp == 255 && u[1].bits.man != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[u[1].bits.sign].f);
+
+ return (x < y ? x : y);
+}
+#endif
+
+
diff --git a/lib/msun/src/s_fminimum_numl.c b/lib/msun/src/s_fminimum_numl.c
new file mode 100644
index 000000000000..6b26d2218d42
--- /dev/null
+++ b/lib/msun/src/s_fminimum_numl.c
@@ -0,0 +1,65 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdbool.h>
+
+#include "fpmath.h"
+
+long double
+fminimum_numl(long double x, long double y)
+{
+ union IEEEl2bits u[2];
+ bool nan_x, nan_y;
+
+ u[0].e = x;
+ mask_nbit_l(u[0]);
+ u[1].e = y;
+ mask_nbit_l(u[1]);
+
+ nan_x = u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0;
+ nan_y = u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0;
+
+ if (nan_x || nan_y) {
+ /* These ternary conditionals force (x+y), so that sNaN's raise exceptions */
+ if (nan_x && nan_y)
+ return (x + y);
+ if (nan_x)
+ return ((x + y) != 0.0 ? y : y);
+ return ((x + y) != 0.0 ? x : x);
+ }
+
+ /* Handle comparisons of signed zeroes. */
+ if (u[0].bits.sign != u[1].bits.sign)
+ return (u[1].bits.sign ? y : x);
+
+ return (x < y ? x : y);
+}
+
+
diff --git a/lib/msun/src/s_fminimumf.c b/lib/msun/src/s_fminimumf.c
index d3978f576931..08ac3ca4c158 100644
--- a/lib/msun/src/s_fminimumf.c
+++ b/lib/msun/src/s_fminimumf.c
@@ -46,7 +46,7 @@ fminimumf(float x, float y)
u[0].f = x;
u[1].f = y;
- /* Check for NaNs to avoid raising spurious exceptions. */
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
if (u[0].bits.exp == 255 && u[0].bits.man != 0 ||
u[1].bits.exp == 255 && u[1].bits.man != 0)
return (NAN);
diff --git a/lib/msun/src/s_fminimuml.c b/lib/msun/src/s_fminimuml.c
index e2c5527ee319..1ef9078ee674 100644
--- a/lib/msun/src/s_fminimuml.c
+++ b/lib/msun/src/s_fminimuml.c
@@ -41,7 +41,7 @@ fminimuml(long double x, long double y)
u[1].e = y;
mask_nbit_l(u[1]);
- /* Check for NaNs to avoid raising spurious exceptions. */
+ /* Handle NaN according to ISO/IEC 60559. NaN argument -> NaN return */
if (u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0 ||
u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0)
return (NAN);
diff --git a/lib/msun/tests/fmaximum_fminimum_test.c b/lib/msun/tests/fmaximum_fminimum_test.c
index 4641f80dfdad..4c8ec9a5b0e0 100644
--- a/lib/msun/tests/fmaximum_fminimum_test.c
+++ b/lib/msun/tests/fmaximum_fminimum_test.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2008 David Schultz <das@FreeBSD.org>
+ * Copyright (c) 2026 Jesús Blázquez <jesuscblazquez@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,7 +26,8 @@
*/
/*
- * Tests for fmaximum{,f,l}() and fminimum{,f,l}()
+ * Tests for fmaximum{,f,l}(), fminimum{,f,l}(), fmaximum_mag{,f,l},
+ * fminimum_mag{,f,l}, fmaximum_num{,f,l}, fminimum_num{,f,l}
*/
#include <sys/cdefs.h>
@@ -58,7 +60,7 @@ testall_r(long double big, long double small, int rmode)
{
long double expected_max, expected_min;
if (isnan(big) || isnan(small)) {
- expected_max = big + small;
+ expected_max = NAN;
expected_min = expected_max;
} else {
expected_max = big;
@@ -79,6 +81,55 @@ testall_r(long double big, long double small, int rmode)
TEST(fminimuml, long double, small, big, expected_min, rmode);
}
+static void
+testall_mag_r(long double big, long double small, int rmode) {
+ long double expected_max_mag, expected_min_mag;
+ if (isnan(big) || isnan(small)) {
+ expected_max_mag = NAN;
+ expected_min_mag = expected_max_mag;
+ } else {
+ if (fabsl(small) > fabsl(big)) {
+ expected_max_mag = small;
+ expected_min_mag = big;
+ } else {
+ expected_max_mag = big;
+ expected_min_mag = small;
+ }
+ }
+
+ TEST(fmaximum_magf, float, big, small, expected_max_mag, rmode);
+ TEST(fmaximum_magf, float, small, big, expected_max_mag, rmode);
+ TEST(fmaximum_mag, double, big, small, expected_max_mag, rmode);
+ TEST(fmaximum_mag, double, small, big, expected_max_mag, rmode);
+ TEST(fmaximum_magl, long double, big, small, expected_max_mag, rmode);
+ TEST(fmaximum_magl, long double, small, big, expected_max_mag, rmode);
+ TEST(fminimum_magf, float, big, small, expected_min_mag, rmode);
+ TEST(fminimum_magf, float, small, big, expected_min_mag, rmode);
+ TEST(fminimum_mag, double, big, small, expected_min_mag, rmode);
+ TEST(fminimum_mag, double, small, big, expected_min_mag, rmode);
+ TEST(fminimum_magl, long double, big, small, expected_min_mag, rmode);
+ TEST(fminimum_magl, long double, small, big, expected_min_mag, rmode);
+}
+
+static void
+testall_num_r(long double big, long double small, int rmode) {
+ long double expected_max_num = isnan(big) ? small : big;
+ long double expected_min_num = isnan(small) ? big : small;
+
+ TEST(fmaximum_numf, float, big, small, expected_max_num, rmode);
+ TEST(fmaximum_numf, float, small, big, expected_max_num, rmode);
+ TEST(fmaximum_num, double, big, small, expected_max_num, rmode);
+ TEST(fmaximum_num, double, small, big, expected_max_num, rmode);
+ TEST(fmaximum_numl, long double, big, small, expected_max_num, rmode);
+ TEST(fmaximum_numl, long double, small, big, expected_max_num, rmode);
+ TEST(fminimum_numf, float, big, small, expected_min_num, rmode);
+ TEST(fminimum_numf, float, small, big, expected_min_num, rmode);
+ TEST(fminimum_num, double, big, small, expected_min_num, rmode);
+ TEST(fminimum_num, double, small, big, expected_min_num, rmode);
+ TEST(fminimum_numl, long double, big, small, expected_min_num, rmode);
+ TEST(fminimum_numl, long double, small, big, expected_min_num, rmode);
+}
+
/*
* Test all the functions: fmaximumf, fmaximum, fmaximuml, fminimumf, fminimum, fminimuml
* in all rounding modes and with the arguments in different orders.
@@ -95,6 +146,8 @@ testall(long double big, long double small)
for (i = 0; i < 4; i++) {
fesetround(rmodes[i]);
testall_r(big, small, rmodes[i]);
+ testall_mag_r(big, small, rmodes[i]);
+ testall_num_r(big, small, rmodes[i]);
}
}
@@ -169,6 +222,24 @@ ATF_TC_BODY(test12, tc)
}
+ATF_TC_WITHOUT_HEAD(test13);
+ATF_TC_BODY(test13, tc)
+{
+ testall(2.0, -2.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test14);
+ATF_TC_BODY(test14, tc)
+{
+ testall(-0.0, -0.0);
+}
+
+ATF_TC_WITHOUT_HEAD(test15);
+ATF_TC_BODY(test15, tc)
+{
+ testall(0.0, 0.0);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, test1);
@@ -183,6 +254,9 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, test10);
ATF_TP_ADD_TC(tp, test11);
ATF_TP_ADD_TC(tp, test12);
+ ATF_TP_ADD_TC(tp, test13);
+ ATF_TP_ADD_TC(tp, test14);
+ ATF_TP_ADD_TC(tp, test15);
return (atf_no_error());
}
diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf
index c4cc6dea02a2..75420e42cdeb 100644
--- a/libexec/rc/rc.conf
+++ b/libexec/rc/rc.conf
@@ -791,7 +791,7 @@ if [ -z "${source_rc_confs_defined}" ]; then
}
fi
-# Allow vendors to override FreeBSD defaults in /etc/default/rc.conf
+# Allow vendors to override FreeBSD defaults in /etc/defaults/rc.conf
# without the need to carefully manage /etc/rc.conf.
if [ -r /etc/defaults/vendor.conf ]; then
. /etc/defaults/vendor.conf
diff --git a/libexec/rc/rc.d/NETWORKING b/libexec/rc/rc.d/NETWORKING
index 402e20927a4c..8f46e78e3426 100755
--- a/libexec/rc/rc.d/NETWORKING
+++ b/libexec/rc/rc.d/NETWORKING
@@ -2,7 +2,7 @@
#
#
-# PROVIDE: NETWORKING NETWORK
+# PROVIDE: NETWORKING
# REQUIRE: netif netwait netoptions routing ppp ipfw stf
# REQUIRE: defaultroute route6d resolv bridge
# REQUIRE: static_arp static_ndp
diff --git a/libexec/rc/rc.d/virtual_oss b/libexec/rc/rc.d/virtual_oss
index d55b51463442..a25abf256f55 100644
--- a/libexec/rc/rc.d/virtual_oss
+++ b/libexec/rc/rc.d/virtual_oss
@@ -25,6 +25,10 @@ required_modules="cuse"
configs=
pidpath="/var/run/${name}"
default_unit=$(sysctl -n hw.snd.default_unit 2> /dev/null)
+
+# Default configuration's control device.
+: "${virtual_oss_default_control_device:="vdsp.ctl"}"
+
virtual_oss_default_args="\
-S \
-C 2 \
@@ -35,7 +39,7 @@ virtual_oss_default_args="\
-i 8 \
-f /dev/dsp${default_unit} \
-d dsp \
- -t vdsp.ctl"
+ -t ${virtual_oss_default_control_device}"
# Set to NO by default. Set it to "YES" to enable virtual_oss.
: "${virtual_oss_enable:="NO"}"
diff --git a/libexec/rc/safe_eval.sh b/libexec/rc/safe_eval.sh
index 6c23b4c98218..e12b17c787d2 100644
--- a/libexec/rc/safe_eval.sh
+++ b/libexec/rc/safe_eval.sh
@@ -1,8 +1,8 @@
:
# RCSid:
-# $Id: safe_eval.sh,v 1.25 2025/08/07 22:13:03 sjg Exp $
+# $Id: safe_eval.sh,v 1.26 2026/04/10 16:03:39 sjg Exp $
#
-# @(#) Copyright (c) 2023-2024 Simon J. Gerraty
+# @(#) Copyright (c) 2023-2026 Simon J. Gerraty
#
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -23,13 +23,16 @@ else
fi
##
-# safe_set
+# safe_set [xtras]
#
# return a safe variable setting
-# any non-alphanumeric chars are replaced with '_'
+# any non-alphanumeric chars other than those in "xtras"
+# will be replaced with '_'
#
+# "xtras" should be used with caution and cannot include ';'
+#
safe_set() {
- ${SED:-sed} 's/[ ]*#.*//;/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "$,/=:+-];_;g'
+ ${SED:-sed} 's/[ ]*#.*//;/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "'"$1"'$,/=:+-];_;g'
}
##
diff --git a/release/Makefile.oracle b/release/Makefile.oracle
deleted file mode 100644
index 6d792cc9fd30..000000000000
--- a/release/Makefile.oracle
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# Makefile for preparing & uploading Oracle Cloud images from existing
-# .raw files created by cloudware-release.
-#
-# Overview:
-#
-# The base image is already created by cloudware-release.
-#
-# Construct the custom OCI metadata, derived from exported official OCI images.
-# It is architecture-specific but appears mostly stable over time.
-# Compress the raw image and place it in the same directory as the metadata.
-# Make a GNU format tarball of these files.
-# Upload the tarball to Oracle Cloud via a pre-approved curl URI, into
-# the FreeBSD Foundation's Oracle Cloud account.
-#
-# These images go into the "re" bucket in us-ashburn-1 region, which
-# is mounted into the FreeBSD Foundation Oracle Marketplace account.
-# Once uploaded, a manual step is needed to import the images as local
-# custom images. These can then be tested within the us-ashburn-1 region.
-# Once tested, follow the manual Oracle Marketplace import process to
-# create a new FreeBSD version, attach the images, and initiate validation
-# by Oracle. This can take up to 5 working days. Once complete, a final
-# manual step is needed to mark the currently private images, public.
-# Syncing to all sites should take 2-3 hours after this final step.
-
-ORACLE_BASENAME= ${OSRELEASE}-${BUILDDATE}${GITREV:C/^(.+)/-\1/}
-CLEANFILES+= cw-oracle-portinstall
-
-cw-oracle-portinstall: .PHONY
-.if (!exists(/usr/local/bin/curl) || !exists(/usr/local/bin/qemu-img)) && !exists(${PORTSDIR}/Makefile)
-. if !exists(/usr/local/sbin/pkg-static)
- env ASSUME_ALWAYS_YES=yes pkg bootstrap -yf
-. endif
-.endif
-.if !exists(/usr/local/bin/curl)
-. if !exists(${PORTSDIR}/Makefile)
- env ASSUME_ALWAYS_YES=yes pkg install -y ftp/curl
-. else
- env UNAME_r=${UNAME_r} make -C \
- ${PORTSDIR}/ftp/curl \
- BATCH=1 WRKDIRPREFIX=/tmp/ports DISTDIR=/tmp/distfiles \
- all install clean
-. endif
-.endif
-.if !exists(/usr/local/bin/qemu-img)
-. if !exists(${PORTSDIR}/Makefile)
- env ASSUME_ALWAYS_YES=yes pkg install -y emulators/qemu@tools
-. else
- env UNAME_r=${UNAME_r} FLAVOR=tools make -C \
- ${PORTSDIR}/emulators/qemu \
- BATCH=1 WRKDIRPREFIX=/tmp/ports DISTDIR=/tmp/distfiles \
- all install clean
-. endif
-.endif
-
-.for _FS in ${ORACLE_FSLIST}
-ORACLE_OCI_LIST+= cw-oracle-${_FS}.oci
-ORACLE_UPLOAD_LIST+= cw-oracle-upload-${_FS}
-CLEANFILES+= cw-oracle-${_FS}.oci
-ORACLE_TMP_${_FS}= cw-oracle-${_FS}.oci.tmpdir
-CLEANDIRS+= ${ORACLE_TMP_${_FS}}
-ORACLE_METADATA= ${.CURDIR}/scripts/oracle
-ORACLE_CAPABILITY= ${.CURDIR}/scripts/oracle/image_capability_data.json
-ORACLE_TEMPLATE= ${.CURDIR}/scripts/oracle/image_metadata.json
-ORACLE_OUTPUT_${_FS}= ${ORACLE_TMP_${_FS}}/image_metadata.json
-.if ${TARGET} == "arm64"
-ORACLE_SHAPES= ${ORACLE_METADATA}/arm64_shape_compatibilities.json
-.else
-ORACLE_SHAPES= ${ORACLE_METADATA}/default_shape_compatibilities.json
-.endif
-
-cw-oracle-${_FS}.oci: cw-oracle-portinstall cw-oracle-${_FS}-raw
- mkdir -p ${ORACLE_TMP_${_FS}}
- # create architecture-specific JSON metadata
- env TYPE="${TYPE}" \
- OSRELEASE="${OSRELEASE}" \
- ORACLE_CAPABILITY="${ORACLE_CAPABILITY}" \
- ORACLE_SHAPES="${ORACLE_SHAPES}" \
- ORACLE_TEMPLATE="${ORACLE_TEMPLATE}" \
- ORACLE_OUTPUT="${ORACLE_OUTPUT_${_FS}}" \
- ${ORACLE_METADATA}/generate_metadata.lua
-
- # convert raw to native qcow2 for zstd compression, saves ~ 8GiB
- qemu-img convert -S 512b -p -O qcow2 -c -o compression_type=zstd \
- ${.OBJDIR}/${ORACLE${_FS:tu}RAWIMAGE} \
- ${ORACLE_TMP_${_FS}}/output.QCOW2
-
- # Create GNU-compatible tarball using BSD tar
- tar --format=gnutar -cf ${.TARGET} -C ${ORACLE_TMP_${_FS}} \
- image_metadata.json output.QCOW2
-
- echo "Oracle image ${.TARGET} is ready for upload."
-
-cw-oracle-upload-${_FS}: cw-oracle-${_FS}.oci
-.if !defined(ORACLE_PAR_URL) || empty(ORACLE_PAR_URL)
- @echo "--------------------------------------------------------------"
- @echo ">>> ORACLE_PAR_URL must be set for Oracle image upload"
- @echo ">>> for testing, use a file:/// URL to a local directory"
- @echo "--------------------------------------------------------------"
- @false
-.endif
- echo "Please wait ... uploading cw-oracle-${_FS}.oci to ${ORACLE_BASENAME}-${_FS}.oci"
- curl -s ${ORACLE_PAR_URL}/${ORACLE_BASENAME}-${_FS}.oci --upload-file cw-oracle-${_FS}.oci
- echo "Uploaded cw-oracle-${_FS}.oci as ${ORACLE_BASENAME}-${_FS}.oci"
- touch ${.TARGET}
-.endfor
-
-cw-oracle-upload: cw-oracle-portinstall ${ORACLE_UPLOAD_LIST}
diff --git a/release/Makefile.vm b/release/Makefile.vm
index abbfcb341afc..d937783f02fe 100644
--- a/release/Makefile.vm
+++ b/release/Makefile.vm
@@ -24,7 +24,6 @@ CLOUDWARE_TYPES?= AZURE \
BASIC-CLOUDINIT \
EC2 \
GCE \
- ORACLE \
VAGRANT
AZURE_FORMAT= vhdf
AZURE_FSLIST?= ufs zfs
@@ -45,9 +44,6 @@ EC2-SMALL_DESC= Amazon EC2 small image
GCE_FORMAT= raw
GCE_FSLIST?= ufs zfs
GCE_DESC= Google Compute Engine image
-ORACLE_FORMAT= raw
-ORACLE_FSLIST?= ufs zfs
-ORACLE_DESC= Oracle Cloud Infrastructure image
OPENSTACK_FORMAT=qcow2
OPENSTACK_FSLIST?= ufs
OPENSTACK_DESC= OpenStack platform image
@@ -315,6 +311,5 @@ cloudware-release:
.include "${.CURDIR}/Makefile.ec2"
.include "${.CURDIR}/Makefile.firecracker"
.include "${.CURDIR}/Makefile.gce"
-.include "${.CURDIR}/Makefile.oracle"
.include "${.CURDIR}/Makefile.vagrant"
.include "${.CURDIR}/Makefile.inc1"
diff --git a/release/packages/ucl/clang.ucl b/release/packages/ucl/clang.ucl
index 3c15d9b7ef03..deee636dc0a7 100644
--- a/release/packages/ucl/clang.ucl
+++ b/release/packages/ucl/clang.ucl
@@ -7,3 +7,8 @@ deps {
version = "${VERSION}"
}
}
+
+shlibs_required_ignore: [
+ "libc.so.7:32",
+ "libgcc_s.so.1:32",
+]
diff --git a/release/release.conf.sample b/release/release.conf.sample
index e583e49828d4..72faef150f88 100644
--- a/release/release.conf.sample
+++ b/release/release.conf.sample
@@ -113,7 +113,7 @@ PORTBRANCH="main"
## If WITH_CLOUDWARE is set to a non-empty value, this is a list of providers
## to create disk images.
-#CLOUDWARE="EC2 GCE ORACLE VAGRANT-VIRTUALBOX VAGRANT-VMWARE"
+#CLOUDWARE="EC2 GCE VAGRANT-VIRTUALBOX VAGRANT-VMWARE"
## If WITH_OCIIMAGES is set to a non-empty value, build Open Container
## Initiative (OCI) base images as part of the release.
diff --git a/release/scripts/oracle/arm64_shape_compatibilities.json b/release/scripts/oracle/arm64_shape_compatibilities.json
deleted file mode 100644
index dfd066b5474f..000000000000
--- a/release/scripts/oracle/arm64_shape_compatibilities.json
+++ /dev/null
@@ -1,24 +0,0 @@
-[
- {
- "internalShapeName": "VM.Standard.A1.Flex",
- "ocpuConstraints": {
- "min": 1,
- "max": 80
- },
- "memoryConstraints": {
- "minInGBs": 1,
- "maxInGBs": 512
- }
- },
- {
- "internalShapeName": "VM.Standard.A2.Flex",
- "ocpuConstraints": {
- "min": 1,
- "max": 78
- },
- "memoryConstraints": {
- "minInGBs": 1,
- "maxInGBs": 946
- }
- }
-]
diff --git a/release/scripts/oracle/default_shape_compatibilities.json b/release/scripts/oracle/default_shape_compatibilities.json
deleted file mode 100644
index fe51488c7066..000000000000
--- a/release/scripts/oracle/default_shape_compatibilities.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
diff --git a/release/scripts/oracle/generate_metadata.lua b/release/scripts/oracle/generate_metadata.lua
deleted file mode 100755
index 751b9680cc29..000000000000
--- a/release/scripts/oracle/generate_metadata.lua
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/libexec/flua
-
-local ucl = require("ucl")
-
--- read from environment variables
-local os_type = os.getenv("TYPE")
-local os_version = os.getenv("OSRELEASE")
--- the raw file
-local capability_file = os.getenv("ORACLE_CAPABILITY")
--- the platform-specific file
-local shapes_file = os.getenv("ORACLE_SHAPES")
--- base template
-local template_file = os.getenv("ORACLE_TEMPLATE")
-local output_file = os.getenv("ORACLE_OUTPUT")
-
-if not os_type or not os_version or not capability_file or
- not shapes_file or not template_file or not output_file then
- io.stderr:write("Error: Oracle metadata script is missing required environment variables:\n")
- io.stderr:write("TYPE, OSRELEASE, ORACLE_CAPABILITY, ORACLE_SHAPES, ORACLE_TEMPLATE, ORACLE_OUTPUT\n")
- os.exit(1)
-end
-
--- read files
-local function read_file(path)
- local f = io.open(path, "r")
- if not f then
- io.stderr:write("Error: Oracle metadata script cannot open file: " .. path .. "\n")
- os.exit(1)
- end
- local content = f:read("*a")
- f:close()
- return content
-end
-
--- parse the template
-local template = read_file(template_file)
-local metadata = ucl.parser()
-metadata:parse_string(template)
-local data = metadata:get_object()
-
--- update the simple fields
-data.operatingSystem = os_type
-data.operatingSystemVersion = os_version
-
--- capability data is actually JSON, but needs to be inserted as a raw blob
-local caps = read_file(capability_file)
--- remove all newlines and preceding spaces to match Oracle's format
-caps = caps:gsub("\n", "")
-caps = caps:gsub("%s+", "")
--- is it still valid JSON?
-local caps_parser = ucl.parser()
-if not caps_parser:parse_string(caps) then
- io.stderr:write("Error: Oracle metadata script found invalid JSON in capability file\n")
- os.exit(1)
-end
--- insert as a raw blob
-data.imageCapabilityData = caps
-
--- parse and insert architecture-dependent shape compatibilities data
-local shapes_data = read_file(shapes_file)
-local shapes = ucl.parser()
-shapes:parse_string(shapes_data)
-data.additionalMetadata.shapeCompatibilities = shapes:get_object()
-
--- save the metadata file
-local dir = os.getenv("PWD")
-local out = io.open(output_file, "w")
-if not out then
- io.stderr:write("Error: Oracle metadata script cannot create output file: "
- .. dir .. "/" .. output_file .. "\n")
- os.exit(1)
-end
-out:write(ucl.to_format(data, "json", {pretty = true}))
-out:close()
diff --git a/release/scripts/oracle/image_capability_data.json b/release/scripts/oracle/image_capability_data.json
deleted file mode 100644
index 01af71f73031..000000000000
--- a/release/scripts/oracle/image_capability_data.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
- "capabilities": {
- "Compute.AMD_SecureEncryptedVirtualization": {
- "descriptorType": "boolean",
- "defaultValue": false
- },
- "Storage.BootVolumeType": {
- "descriptorType": "enumstring",
- "values": [
- "ISCSI",
- "PARAVIRTUALIZED",
- "SCSI",
- "IDE",
- "NVME"
- ],
- "defaultValue": "PARAVIRTUALIZED"
- },
- "Storage.Iscsi.MultipathDeviceSupported": {
- "descriptorType": "boolean",
- "defaultValue": false
- },
- "Storage.ParaVirtualization.EncryptionInTransit": {
- "descriptorType": "boolean",
- "defaultValue": true
- },
- "Storage.ConsistentVolumeNaming": {
- "descriptorType": "boolean",
- "defaultValue": true
- },
- "Compute.SecureBoot": {
- "descriptorType": "boolean",
- "defaultValue": false
- },
- "Storage.ParaVirtualization.AttachmentVersion": {
- "descriptorType": "enuminteger",
- "values": [
- 1,
- 2
- ],
- "defaultValue": 2
- },
- "Storage.LocalDataVolumeType": {
- "descriptorType": "enumstring",
- "values": [
- "ISCSI",
- "PARAVIRTUALIZED",
- "SCSI",
- "IDE",
- "NVME"
- ],
- "defaultValue": "PARAVIRTUALIZED"
- },
- "Network.AttachmentType": {
- "descriptorType": "enumstring",
- "values": [
- "PARAVIRTUALIZED",
- "VDPA"
- ],
- "defaultValue": "PARAVIRTUALIZED"
- },
- "Storage.RemoteDataVolumeType": {
- "descriptorType": "enumstring",
- "values": [
- "ISCSI",
- "PARAVIRTUALIZED",
- "SCSI",
- "IDE",
- "NVME"
- ],
- "defaultValue": "PARAVIRTUALIZED"
- },
- "Compute.LaunchMode": {
- "descriptorType": "enumstring",
- "values": [
- "NATIVE",
- "EMULATED",
- "VDPA",
- "PARAVIRTUALIZED",
- "CUSTOM"
- ],
- "defaultValue": "PARAVIRTUALIZED"
- },
- "Network.IPv6Only": {
- "descriptorType": "boolean",
- "defaultValue": false
- },
- "Compute.Firmware": {
- "descriptorType": "enumstring",
- "values": [
- "BIOS",
- "UEFI_64"
- ],
- "defaultValue": "UEFI_64"
- }
- }
-}
diff --git a/release/scripts/oracle/image_metadata.json b/release/scripts/oracle/image_metadata.json
deleted file mode 100644
index eaea3dd1cad2..000000000000
--- a/release/scripts/oracle/image_metadata.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "version": 2,
- "externalLaunchOptions": {
- "firmware": "UEFI_64",
- "networkType": "PARAVIRTUALIZED",
- "bootVolumeType": "PARAVIRTUALIZED",
- "remoteDataVolumeType": "PARAVIRTUALIZED",
- "localDataVolumeType": "PARAVIRTUALIZED",
- "launchOptionsSource": "PARAVIRTUALIZED",
- "pvAttachmentVersion": 2,
- "pvEncryptionInTransitEnabled": false,
- "consistentVolumeNamingEnabled": false
- },
- "imageCapabilityData": "REPLACE",
- "imageCapsFormatVersion": "23cfd738-ad9c-4f56-9281-67be6c8cd14c",
- "operatingSystem": "REPLACE",
- "operatingSystemVersion": "REPLACE",
- "additionalMetadata": {
- "shapeCompatibilities": "REPLACE"
- }
-}
diff --git a/release/tools/ec2-builder.conf b/release/tools/ec2-builder.conf
index a272ea49a426..3b0344f9eb9a 100644
--- a/release/tools/ec2-builder.conf
+++ b/release/tools/ec2-builder.conf
@@ -17,7 +17,6 @@ vm_extra_filter_base_packages() {
-e '.*-dbg$' \
-e '.*-lib32$' \
-e '^FreeBSD-set-tests'
- echo FreeBSD-clibs-lib32
}
# Packages to install into the image we're creating. In addition to packages
diff --git a/release/tools/ec2-small.conf b/release/tools/ec2-small.conf
index c1a05f98356f..6564a59c2cf6 100644
--- a/release/tools/ec2-small.conf
+++ b/release/tools/ec2-small.conf
@@ -20,7 +20,6 @@ vm_extra_filter_base_packages() {
-e '.*-dbg$' \
-e '.*-lib32$' \
-e '^FreeBSD-set-tests'
- echo FreeBSD-clibs-lib32
}
# Packages to install into the image we're creating. In addition to packages
diff --git a/release/tools/oracle.conf b/release/tools/oracle.conf
deleted file mode 100644
index b289f4e4e7e7..000000000000
--- a/release/tools/oracle.conf
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/bin/sh
-# Set to a list of packages to install.
-export VM_EXTRA_PACKAGES="
- comms/py-pyserial
- converters/base64
- devel/oci-cli
- devel/py-babel
- devel/py-iso8601
- devel/py-pbr
- devel/py-six
- ftp/curl
- lang/python
- lang/python3
- net/cloud-init
- net/py-eventlet
- net/py-netaddr
- net/py-netifaces
- net/py-oauth
- net/rsync
- security/ca_root_nss
- security/sudo@default
- sysutils/firstboot-freebsd-update
- sysutils/firstboot-pkgs
- sysutils/panicmail
- textproc/jq
- "
-
-# Should be enough for base image, image can be resized in needed
-export VMSIZE=8g
-
-# Set to a list of third-party software to enable in rc.conf(5).
-export VM_RC_LIST="
- cloudinit
- firstboot_pkgs
- firstboot_freebsd_update
- growfs
- ntpd
- ntpd_sync_on_start
- sshd
- zfs"
-
-# Hack for FreeBSD 15.0; should go away before 15.1.
-MISSING_METALOGS="
-./usr/local/etc/cloud/cloud.cfg
-./usr/local/etc/cloud/cloud.cfg.d/05_logging.cfg
-./usr/local/etc/cloud/cloud.cfg.d/99_freebsd.cfg
-./usr/local/etc/pam.d/sudo
-./usr/local/etc/rsync/rsyncd.conf
-./usr/local/etc/ssl/cert.pem
-./usr/local/etc/sudo.conf
-./usr/local/etc/sudo_logsrvd.conf
-./usr/local/etc/sudoers
-"
-
-vm_extra_pre_umount() {
- cat <<-'EOF' >> ${DESTDIR}/etc/rc.conf
- dumpdev=AUTO
-EOF
-
- cat <<-'EOF' >> ${DESTDIR}/boot/loader.conf
- autoboot_delay="5"
- beastie_disable="YES"
- boot_serial="YES"
- loader_logo="none"
- cryptodev_load="YES"
- opensolaris_load="YES"
- xz_load="YES"
- zfs_load="YES"
-EOF
- metalog_add_data ./boot/loader.conf
-
- cat <<-'EOF' >> ${DESTDIR}/etc/ssh/sshd_config
- # S11 Configure the SSH service to prevent password-based login
- PermitRootLogin prohibit-password
- PasswordAuthentication no
- KbdInteractiveAuthentication no
- PermitEmptyPasswords no
- UseDNS no
-EOF
-
- # S14 Root user login must be disabled on serial-over-ssh console
- pw -R ${DESTDIR} usermod root -w no
- # Oracle requirements override the default FreeBSD cloud-init settings
- cat <<-'EOF' >> ${DESTDIR}/usr/local/etc/cloud/cloud.cfg.d/98_oracle.cfg
- disable_root: true
- system_info:
- distro: freebsd
- default_user:
- name: freebsd
- lock_passwd: True
- gecos: "Oracle Cloud Default User"
- groups: [wheel]
- sudo: ["ALL=(ALL) NOPASSWD:ALL"]
- shell: /bin/sh
- network:
- renderers: ['freebsd']
-EOF
- metalog_add_data ./usr/local/etc/cloud/cloud.cfg.d/98_oracle.cfg
-
- # Use Oracle Cloud Infrastructure NTP server
- sed -i '' -E -e 's/^pool.*iburst/server 169.254.169.254 iburst/' \
- ${DESTDIR}/etc/ntp.conf
-
- return 0
-}
diff --git a/sbin/devd/snd.conf b/sbin/devd/snd.conf
index 5ca0be86e246..e2dc6d94a299 100644
--- a/sbin/devd/snd.conf
+++ b/sbin/devd/snd.conf
@@ -7,12 +7,8 @@ notify 0 {
# Other audio servers or device switching commands can be used here
# instead of virtual_oss(8).
- #
- # FIXME: We are hardcoding /dev/vdsp.ctl here, simply because it is a
- # common virtual_oss control device name. Until we find a proper way to
- # define control devices here, /dev/vdsp.ctl can be changed to the
- # control device of choice.
- action "/usr/sbin/virtual_oss_cmd /dev/vdsp.ctl -R /dev/$cdev";
+ action "/usr/sbin/virtual_oss_cmd \
+ /dev/$(sysrc virtual_oss_default_control_device) -R /dev/$cdev";
};
notify 0 {
@@ -22,7 +18,8 @@ notify 0 {
match "cdev" "dsp[0-9]+";
# See comment above.
- action "/usr/sbin/virtual_oss_cmd /dev/vdsp.ctl -P /dev/$cdev";
+ action "/usr/sbin/virtual_oss_cmd \
+ /dev/$(sysrc virtual_oss_default_control_device) -P /dev/$cdev";
};
notify 0 {
@@ -32,5 +29,6 @@ notify 0 {
# No connected devices. Disable both recording and playback to avoid
# repeated virtual_oss error messages.
- action "/usr/sbin/virtual_oss_cmd /dev/vdsp.ctl -f /dev/null";
+ action "/usr/sbin/virtual_oss_cmd \
+ /dev/$(sysrc virtual_oss_default_control_device) -f /dev/null";
};
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
index 2553e61b5e9e..26391023d54a 100644
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -52,10 +52,6 @@ SRCS+= ifpfsync.c # pfsync(4) support
SRCS+= ifbridge.c # bridge support
SRCS+= iflagg.c # lagg support
-.if ${MK_EXPERIMENTAL} != "no"
-CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
-CFLAGS+= -DEXPERIMENTAL
-.endif
.if ${MK_INET6_SUPPORT} != "no"
CFLAGS+= -DINET6
.endif
@@ -70,6 +66,7 @@ LIBADD+= nv
.if ${MK_NETLINK_SUPPORT} != "no"
SRCS+= ifconfig_netlink.c
+SRCS+= ifgeneve.c # GENEVE support
.else
CFLAGS+=-DWITHOUT_NETLINK
.endif
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
index 365f01be9590..492ee5bbbed0 100644
--- a/sbin/ifconfig/af_inet6.c
+++ b/sbin/ifconfig/af_inet6.c
@@ -730,10 +730,6 @@ static struct cmd inet6_cmds[] = {
DEF_CMD("eui64", 0, setip6eui64),
DEF_CMD("stableaddr", ND6_IFF_STABLEADDR, setnd6flags),
DEF_CMD("-stableaddr", -ND6_IFF_STABLEADDR, setnd6flags),
-#ifdef EXPERIMENTAL
- DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
- DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
-#endif
};
static struct afswtch af_inet6 = {
diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c
index fb7e72028e2e..199523450dca 100644
--- a/sbin/ifconfig/af_nd6.c
+++ b/sbin/ifconfig/af_nd6.c
@@ -62,10 +62,6 @@ static const char *ND6BITS[] = {
[6] = "NO_RADR",
[7] = "NO_PREFER_IFACE",
[8] = "NO_DAD",
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- [9] = "IPV6_ONLY",
- [10] = "IPV6_ONLY_MANUAL",
-#endif
[11] = "STABLEADDR",
[15] = "DEFAULTIF",
};
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index f062ddec774d..1dc599a61623 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 12, 2025
+.Dd December 11, 2025
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -3257,6 +3257,117 @@ Delete all dynamically-learned addresses from the forwarding table.
.It Cm vxlanflushall
Delete all addresses, including static addresses, from the forwarding table.
.El
+.Ss Generic Network Virtualization Encapsulation Parameters
+The following parameters are used to configure
+.Xr geneve 4
+interfaces.
+.Bl -tag -width indent
+.It Cm geneveid Ar identifier
+This value is a 24-bit Virtual Network Identifier (VNI) that identifies the
+virtual network identifier of the interface.
+.It Cm genevemode Ar mode
+Set the
+.Nm
+protocol operating
+.Ar mode
+value.
+Supported modes are currently:
+.Bl -tag -width indent
+.It Cm l2
+Default.
+.It Cm l3
+.El
+.It Cm genevelocal Ar address
+The source address used in the encapsulating IPv4/IPv6 header.
+The address should already be assigned to an existing interface.
+When the interface is configured in unicast mode, the listening socket
+is bound to this address.
+.It Cm geneveremote Ar address
+The interface can be configured in a unicast, or point-to-point, mode
+to create a tunnel between two hosts.
+This is the IP address of the remote end of the tunnel.
+.It Cm genevegroup Ar address
+The interface can be configured in a multicast mode
+to create a virtual network of hosts.
+This is the IP multicast group address the interface will join.
+.It Cm genevelocalport Ar port
+The port number the interface will listen on.
+The default port number is 6081.
+.It Cm geneveremoteport Ar port
+The destination port number used in the encapsulating IPv4/IPv6 header.
+The remote host should be listening on this port.
+The default port number is 6081.
+.It Cm geneveportrange Ar low high
+The range of source ports used in the encapsulating IPv4/IPv6 header.
+The port selected within the range is based on a hash of the inner frame.
+A range is useful to provide entropy within the outer IP header
+for more effective load balancing.
+The default range is between the
+.Xr sysctl 8
+variables
+.Va net.inet.ip.portrange.first
+and
+.Va net.inet.ip.portrange.last
+.It Cm genevetimeout Ar timeout
+The maximum time, in seconds, before an entry in the forwarding table
+is pruned.
+The default is 1200 seconds (20 minutes).
+.It Cm genevemaxaddr Ar max
+The maximum number of entries in the forwarding table.
+The default is 2000.
+.It Cm genevedev Ar dev
+When the interface is configured in multicast mode, the
+.Cm dev
+interface is used to transmit IP multicast packets.
+.It Cm genevedf Ar df
+Set the Do not fragment (DF) bit in the encapsulating header.
+Supported
+.Ar df
+values are currently:
+.Bl -tag -width indent
+.It Cm set
+Do not allow fragmentation on the output IPv4/IPv6 packets and
+set the Do not fragment (DF) bit in the encapsulating IPv4 header.
+.It Cm unset
+Default.
+.It Cm inherit
+The Do not fragment (DF) bit copied from inner IPv4 header to the
+outer IPv4 header.
+.El
+.It Cm genevettl Ar ttl
+The TTL used in the encapsulating IPv4/IPv6 header.
+.Bl -tag -width indent
+.It Cm 0-255
+The default is 64.
+.It Cm inherit
+The TTL copied from inner encapsulated header to the outer header.
+.El
+.It Cm genevedscpinherit
+Inherit DSCP or Traffic Class value from the inner IPv4/IPv6 header.
+.It Fl genevedscpinherit
+Unconfigure DSCP or Traffic Class inheritence from the inner IPv4/IPv6 header.
+This is the default.
+.It Cm genevelearn
+When in L2 unicast mode, The source IP address and inner source Ethernet
+MAC address of received packets are used to dynamically populate the
+forwarding table.
+When in L2 multicast mode, an entry in the forwarding table allows the
+interface to send the frame directly to the remote host instead of
+broadcasting the frame to the multicast group.
+This is the default.
+.It Fl genevelearn
+In L2 mode, geneve forwarding table is not populated by received packets.
+.It Cm geneveexternal
+make this tunnel externally controlled.
+.It Fl geneveexternal
+enable manual configuration for this tunnel.
+This is the default
+.It Cm geneveflush
+Delete all dynamically-learned addresses from the forwarding table when in L2
+mode.
+.It Cm geneveflushall
+Delete all addresses, including static addresses, from the forwarding table.
+.El
.Ss CARP Parameters
The following parameters are used to configure
.Xr carp 4
diff --git a/sbin/ifconfig/ifgeneve.c b/sbin/ifconfig/ifgeneve.c
new file mode 100644
index 000000000000..db0e90e6f278
--- /dev/null
+++ b/sbin/ifconfig/ifgeneve.c
@@ -0,0 +1,889 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/nv.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_strings.h>
+#include <netinet/in.h>
+#include <net/if_geneve.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+
+#include "ifconfig.h"
+#include "ifconfig_netlink.h"
+
+struct nl_parsed_geneve {
+ /* essential */
+ uint32_t ifla_vni;
+ uint16_t ifla_proto;
+ struct sockaddr *ifla_local;
+ struct sockaddr *ifla_remote;
+ uint16_t ifla_local_port;
+ uint16_t ifla_remote_port;
+
+ /* optional */
+ struct ifla_geneve_port_range *ifla_port_range;
+ enum ifla_geneve_df ifla_df;
+ uint8_t ifla_ttl;
+ bool ifla_ttl_inherit;
+ bool ifla_dscp_inherit;
+ bool ifla_external;
+
+ /* l2 specific */
+ bool ifla_ftable_learn;
+ bool ifla_ftable_flush;
+ uint32_t ifla_ftable_max;
+ uint32_t ifla_ftable_timeout;
+ uint32_t ifla_ftable_count;
+ uint32_t ifla_ftable_nospace;
+ uint32_t ifla_ftable_lock_upgrade_failed;
+
+ /* multicast specific */
+ char *ifla_mc_ifname;
+ uint32_t ifla_mc_ifindex;
+
+ /* csum info */
+ uint64_t ifla_stats_txcsum;
+ uint64_t ifla_stats_tso;
+ uint64_t ifla_stats_rxcsum;
+};
+
+static struct geneve_params gnvp = {
+ .ifla_proto = GENEVE_PROTO_ETHER,
+};
+
+static int
+get_proto(const char *cp, uint16_t *valp)
+{
+ uint16_t val;
+
+ if (!strcmp(cp, "l2"))
+ val = GENEVE_PROTO_ETHER;
+ else if (!strcmp(cp, "l3"))
+ val = GENEVE_PROTO_INHERIT;
+ else
+ return (-1);
+
+ *valp = val;
+ return (0);
+}
+
+static int
+get_val(const char *cp, u_long *valp)
+{
+ char *endptr;
+ u_long val;
+
+ errno = 0;
+ val = strtoul(cp, &endptr, 0);
+ if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
+ return (-1);
+
+ *valp = val;
+ return (0);
+}
+
+static int
+get_df(const char *cp, enum ifla_geneve_df *valp)
+{
+ enum ifla_geneve_df df;
+
+ if (!strcmp(cp, "set"))
+ df = IFLA_GENEVE_DF_SET;
+ else if (!strcmp(cp, "inherit"))
+ df = IFLA_GENEVE_DF_INHERIT;
+ else if (!strcmp(cp, "unset"))
+ df = IFLA_GENEVE_DF_UNSET;
+ else
+ return (-1);
+
+ *valp = df;
+ return (0);
+}
+
+static bool
+is_multicast(struct addrinfo *ai)
+{
+#if (defined INET || defined INET6)
+ struct sockaddr *sa;
+ sa = ai->ai_addr;
+#endif
+
+ switch (ai->ai_family) {
+#ifdef INET
+ case AF_INET: {
+ struct sockaddr_in *sin = satosin(sa);
+
+ return (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)));
+ }
+#endif
+#ifdef INET6
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = satosin6(sa);
+
+ return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr));
+ }
+#endif
+ default:
+ errx(1, "address family not supported");
+ }
+}
+
+/*
+ * geneve mode is read-only after creation,
+ * therefore there is no need for separate netlink implementation
+ */
+static void
+setgeneve_mode_clone(if_ctx *ctx __unused, const char *arg, int dummy __unused)
+{
+ uint16_t val;
+
+ if (get_proto(arg, &val) < 0)
+ errx(1, "invalid inner protocol: %s", arg);
+
+ gnvp.ifla_proto = val;
+}
+
+struct nla_geneve_info {
+ const char *kind;
+ struct nl_parsed_geneve data;
+};
+
+struct nla_geneve_link {
+ uint32_t ifi_index;
+ struct nla_geneve_info linkinfo;
+};
+
+static inline void
+geneve_nl_init(if_ctx *ctx, struct snl_writer *nw, uint32_t flags)
+{
+ struct nlmsghdr *hdr;
+
+ snl_init_writer(ctx->io_ss, nw);
+ hdr = snl_create_msg_request(nw, NL_RTM_NEWLINK);
+ hdr->nlmsg_flags |= flags;
+ snl_reserve_msg_object(nw, struct ifinfomsg);
+ snl_add_msg_attr_string(nw, IFLA_IFNAME, ctx->ifname);
+}
+
+static inline void
+geneve_nl_fini(if_ctx *ctx, struct snl_writer *nw)
+{
+ struct nlmsghdr *hdr;
+ struct snl_errmsg_data errmsg = {};
+
+ hdr = snl_finalize_msg(nw);
+ if (hdr == NULL || !snl_send_message(ctx->io_ss, hdr))
+ err(1, "unable to send netlink message");
+
+ if (!snl_read_reply_code(ctx->io_ss, hdr->nlmsg_seq, &errmsg))
+ errx(errmsg.error, "%s", errmsg.error_str);
+}
+
+#define _OUT(_field) offsetof(struct nl_parsed_geneve, _field)
+static const struct snl_attr_parser nla_geneve_linkinfo_data[] = {
+ { .type = IFLA_GENEVE_ID, .off = _OUT(ifla_vni), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_PROTOCOL, .off = _OUT(ifla_proto), .cb = snl_attr_get_uint16 },
+ { .type = IFLA_GENEVE_LOCAL, .off = _OUT(ifla_local), .cb = snl_attr_get_ip },
+ { .type = IFLA_GENEVE_REMOTE, .off = _OUT(ifla_remote), .cb = snl_attr_get_ip },
+ { .type = IFLA_GENEVE_LOCAL_PORT, .off = _OUT(ifla_local_port), .cb = snl_attr_get_uint16 },
+ { .type = IFLA_GENEVE_PORT, .off = _OUT(ifla_remote_port), .cb = snl_attr_get_uint16 },
+ { .type = IFLA_GENEVE_PORT_RANGE, .off = _OUT(ifla_port_range), .cb = snl_attr_dup_struct },
+ { .type = IFLA_GENEVE_DF, .off = _OUT(ifla_df), .cb = snl_attr_get_uint8 },
+ { .type = IFLA_GENEVE_TTL, .off = _OUT(ifla_ttl), .cb = snl_attr_get_uint8 },
+ { .type = IFLA_GENEVE_TTL_INHERIT, .off = _OUT(ifla_ttl_inherit), .cb = snl_attr_get_bool },
+ { .type = IFLA_GENEVE_DSCP_INHERIT, .off = _OUT(ifla_dscp_inherit), .cb = snl_attr_get_bool },
+ { .type = IFLA_GENEVE_COLLECT_METADATA, .off = _OUT(ifla_external), .cb = snl_attr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_LEARN, .off = _OUT(ifla_ftable_learn), .cb = snl_attr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_FLUSH, .off = _OUT(ifla_ftable_flush), .cb = snl_attr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_MAX, .off = _OUT(ifla_ftable_max), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_FTABLE_TIMEOUT, .off = _OUT(ifla_ftable_timeout), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_FTABLE_COUNT, .off = _OUT(ifla_ftable_count), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_FTABLE_NOSPACE_CNT, .off = _OUT(ifla_ftable_nospace), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_FTABLE_LOCK_UP_FAIL_CNT, .off = _OUT(ifla_ftable_lock_upgrade_failed), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_MC_IFNAME, .off = _OUT(ifla_mc_ifname), .cb = snl_attr_get_string },
+ { .type = IFLA_GENEVE_MC_IFINDEX, .off = _OUT(ifla_mc_ifindex), .cb = snl_attr_get_uint32 },
+ { .type = IFLA_GENEVE_TXCSUM_CNT, .off = _OUT(ifla_stats_txcsum), .cb = snl_attr_get_uint64 },
+ { .type = IFLA_GENEVE_TSO_CNT, .off = _OUT(ifla_stats_tso), .cb = snl_attr_get_uint64 },
+ { .type = IFLA_GENEVE_RXCSUM_CNT, .off = _OUT(ifla_stats_rxcsum), .cb = snl_attr_get_uint64 },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER(geneve_linkinfo_data_parser, nla_geneve_linkinfo_data);
+
+#define _OUT(_field) offsetof(struct nla_geneve_info, _field)
+static const struct snl_attr_parser ap_geneve_linkinfo[] = {
+ { .type = IFLA_INFO_KIND, .off = _OUT(kind), .cb = snl_attr_get_string },
+ { .type = IFLA_INFO_DATA, .off = _OUT(data),
+ .arg = &geneve_linkinfo_data_parser, .cb = snl_attr_get_nested },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER(geneve_linkinfo_parser, ap_geneve_linkinfo);
+
+#define _IN(_field) offsetof(struct ifinfomsg, _field)
+#define _OUT(_field) offsetof(struct nla_geneve_link, _field)
+static const struct snl_attr_parser ap_geneve_link[] = {
+ { .type = IFLA_LINKINFO, .off = _OUT(linkinfo),
+ .arg = &geneve_linkinfo_parser, .cb = snl_attr_get_nested },
+};
+
+static const struct snl_field_parser fp_geneve_link[] = {
+ { .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(geneve_parser, struct ifinfomsg, fp_geneve_link, ap_geneve_link);
+
+static const struct snl_hdr_parser *all_parsers[] = {
+ &geneve_linkinfo_data_parser,
+ &geneve_linkinfo_parser,
+ &geneve_parser,
+};
+
+static void
+geneve_status_nl(if_ctx *ctx)
+{
+ struct snl_writer nw;
+ struct nlmsghdr *hdr;
+ struct snl_errmsg_data errmsg;
+ struct nla_geneve_link geneve_link;
+ char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];
+ struct sockaddr *lsa, *rsa;
+ int mc;
+ bool ipv6 = false;
+
+ if (strncmp(ctx->ifname, "geneve", sizeof("geneve") - 1) != 0)
+ return;
+
+ snl_init_writer(ctx->io_ss, &nw);
+ hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+ snl_reserve_msg_object(&nw, struct ifinfomsg);
+ snl_add_msg_attr_string(&nw, IFLA_IFNAME, ctx->ifname);
+
+ if (!(hdr = snl_finalize_msg(&nw)) || (!snl_send_message(ctx->io_ss, hdr)))
+ return;
+
+ hdr = snl_read_reply(ctx->io_ss, hdr->nlmsg_seq);
+ if (hdr->nlmsg_type != NL_RTM_NEWLINK) {
+ if (!snl_parse_errmsg(ctx->io_ss, hdr, &errmsg))
+ errx(EINVAL, "(NETLINK)");
+ if (errmsg.error_str != NULL)
+ errx(errmsg.error, "(NETLINK) %s", errmsg.error_str);
+ }
+
+ if (!snl_parse_nlmsg(ctx->io_ss, hdr, &geneve_parser, &geneve_link))
+ return;
+
+ struct nla_geneve_info geneve_info = geneve_link.linkinfo;
+ struct nl_parsed_geneve geneve_data = geneve_info.data;
+
+ printf("\tgeneve mode: ");
+ switch (geneve_data.ifla_proto) {
+ case GENEVE_PROTO_INHERIT:
+ printf("l3");
+ break;
+ case GENEVE_PROTO_ETHER:
+ default:
+ printf("l2");
+ break;
+ }
+
+ printf("\n\tgeneve config:\n");
+ /* Just report nothing if the network identity isn't set yet. */
+ if (geneve_data.ifla_vni >= GENEVE_VNI_MAX) {
+ printf("\t\tvirtual network identifier (vni): not configured\n");
+ return;
+ }
+
+ lsa = geneve_data.ifla_local;
+ rsa = geneve_data.ifla_remote;
+
+ if ((lsa == NULL) ||
+ (getnameinfo(lsa, lsa->sa_len, src, sizeof(src),
+ NULL, 0, NI_NUMERICHOST) != 0))
+ src[0] = '\0';
+ if ((rsa == NULL) ||
+ (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst),
+ NULL, 0, NI_NUMERICHOST) != 0))
+ dst[0] = '\0';
+ else {
+ ipv6 = rsa->sa_family == AF_INET6;
+ if (!ipv6) {
+ struct sockaddr_in *sin = satosin(rsa);
+ mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr));
+ } else {
+ struct sockaddr_in6 *sin6 = satosin6(rsa);
+ mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr);
+ }
+ }
+
+ printf("\t\tvirtual network identifier (vni): %d", geneve_data.ifla_vni);
+ if (src[0] != '\0')
+ printf("\n\t\tlocal: %s%s%s:%u", ipv6 ? "[" : "", src, ipv6 ? "]" : "",
+ geneve_data.ifla_local_port);
+ if (dst[0] != '\0') {
+ printf("\n\t\t%s: %s%s%s:%u", mc ? "group" : "remote", ipv6 ? "[" : "",
+ dst, ipv6 ? "]" : "", geneve_data.ifla_local_port);
+ if (mc)
+ printf(", dev: %s", geneve_data.ifla_mc_ifname);
+ }
+
+ if (ctx->args->verbose) {
+ printf("\n\t\tportrange: %u-%u",
+ geneve_data.ifla_port_range->low,
+ geneve_data.ifla_port_range->high);
+
+ if (geneve_data.ifla_ttl_inherit)
+ printf(", ttl: inherit");
+ else
+ printf(", ttl: %d", geneve_data.ifla_ttl);
+
+ if (geneve_data.ifla_dscp_inherit)
+ printf(", dscp: inherit");
+
+ if (geneve_data.ifla_df == IFLA_GENEVE_DF_INHERIT)
+ printf(", df: inherit");
+ else if (geneve_data.ifla_df == IFLA_GENEVE_DF_SET)
+ printf(", df: set");
+ else if (geneve_data.ifla_df == IFLA_GENEVE_DF_UNSET)
+ printf(", df: unset");
+
+ if (geneve_data.ifla_external)
+ printf(", externally controlled");
+
+ if (geneve_data.ifla_proto == GENEVE_PROTO_ETHER) {
+ printf("\n\t\tftable mode: %slearning",
+ geneve_data.ifla_ftable_learn ? "" : "no");
+ printf(", count: %d, max: %d, timeout: %d",
+ geneve_data.ifla_ftable_count,
+ geneve_data.ifla_ftable_max,
+ geneve_data.ifla_ftable_timeout);
+ printf(", nospace: %u",
+ geneve_data.ifla_ftable_nospace);
+ }
+
+ printf("\n\t\tstats: tso %ju, txcsum %ju, rxcsum %ju",
+ (uintmax_t)geneve_data.ifla_stats_tso,
+ (uintmax_t)geneve_data.ifla_stats_txcsum,
+ (uintmax_t)geneve_data.ifla_stats_rxcsum);
+ }
+
+ putchar('\n');
+}
+
+
+static void
+geneve_create_nl(if_ctx *ctx, struct ifreq *ifr)
+{
+ struct snl_writer nw = {};
+ struct nlmsghdr *hdr;
+ int off, off2;
+
+ snl_init_writer(ctx->io_ss, &nw);
+ hdr = snl_create_msg_request(&nw, RTM_NEWLINK);
+ hdr->nlmsg_flags |= (NLM_F_CREATE | NLM_F_EXCL);
+ snl_reserve_msg_object(&nw, struct ifinfomsg);
+ snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifr->ifr_name);
+
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+ snl_add_msg_attr_u16(&nw, IFLA_GENEVE_PROTOCOL, gnvp.ifla_proto);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_vni_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || val >= GENEVE_VNI_MAX)
+ errx(1, "invalid network identifier: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+ snl_add_msg_attr_u32(&nw, IFLA_GENEVE_ID, val);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_local_nl(if_ctx *ctx, const char *addr, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ struct addrinfo *ai;
+ const struct sockaddr *sa;
+ int error;
+
+ if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
+ errx(1, "error in parsing local address string: %s",
+ gai_strerror(error));
+
+ if (is_multicast(ai))
+ errx(1, "local address cannot be multicast");
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ sa = ai->ai_addr;
+ snl_add_msg_attr_ip(&nw, IFLA_GENEVE_LOCAL, sa);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_remote_nl(if_ctx *ctx, const char *addr, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ struct addrinfo *ai;
+ const struct sockaddr *sa;
+ int error;
+
+ if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
+ errx(1, "error in parsing remote address string: %s",
+ gai_strerror(error));
+
+ if (is_multicast(ai))
+ errx(1, "remote address cannot be multicast");
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ sa = ai->ai_addr;
+ snl_add_msg_attr_ip(&nw, IFLA_GENEVE_REMOTE, sa);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_group_nl(if_ctx *ctx, const char *addr, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ struct addrinfo *ai;
+ struct sockaddr *sa;
+ int error;
+
+ if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0)
+ errx(1, "error in parsing local address string: %s",
+ gai_strerror(error));
+
+ if (!is_multicast(ai))
+ errx(1, "group address must be multicast");
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ sa = ai->ai_addr;
+ snl_add_msg_attr_ip(&nw, IFLA_GENEVE_REMOTE, sa);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+
+static void
+setgeneve_local_port_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
+ errx(1, "invalid local port: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_u16(&nw, IFLA_GENEVE_LOCAL_PORT, val);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_remote_port_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || val >= UINT16_MAX)
+ errx(1, "invalid remote port: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_u16(&nw, IFLA_GENEVE_PORT, val);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_port_range_nl(if_ctx *ctx, const char *arg1, const char *arg2)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long min, max;
+
+ if (get_val(arg1, &min) < 0 || min >= UINT16_MAX)
+ errx(1, "invalid port range minimum: %s", arg1);
+ if (get_val(arg2, &max) < 0 || max >= UINT16_MAX)
+ errx(1, "invalid port range maximum: %s", arg2);
+ if (max < min)
+ errx(1, "invalid port range");
+
+ const struct ifla_geneve_port_range port_range = {
+ .low = min,
+ .high = max
+ };
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr(&nw, IFLA_GENEVE_PORT_RANGE,
+ sizeof(port_range), (const void *)&port_range);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_timeout_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
+ errx(1, "invalid timeout value: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_u32(&nw, IFLA_GENEVE_FTABLE_TIMEOUT, val);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_maxaddr_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0)
+ errx(1, "invalid maxaddr value: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_u32(&nw, IFLA_GENEVE_FTABLE_MAX, val);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_dev_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_string(&nw, IFLA_GENEVE_MC_IFNAME, arg);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_ttl_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ u_long val;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+ if ((get_val(arg, &val) < 0 || val > 256) == 0) {
+ snl_add_msg_attr_u8(&nw, IFLA_GENEVE_TTL, val);
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_TTL_INHERIT, false);
+ } else if (!strcmp(arg, "inherit")) {
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_TTL_INHERIT, true);
+ } else
+ errx(1, "invalid TTL value: %s", arg);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_df_nl(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+ enum ifla_geneve_df df;
+
+ if (get_df(arg, &df) < 0)
+ errx(1, "invalid df value: %s", arg);
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_u8(&nw, IFLA_GENEVE_DF, df);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_inherit_dscp_nl(if_ctx *ctx, const char *arg __unused, int d)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_DSCP_INHERIT, d != 0);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_learn_nl(if_ctx *ctx, const char *arg __unused, int d)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_FTABLE_LEARN, d != 0);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_flush_nl(if_ctx *ctx, const char *val __unused, int d)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_FTABLE_FLUSH, d != 0);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static void
+setgeneve_external_nl(if_ctx *ctx, const char *val __unused, int d)
+{
+ struct snl_writer nw = {};
+ int off, off2;
+
+ geneve_nl_init(ctx, &nw, 0);
+ off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+ snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "geneve");
+
+ off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+ snl_add_msg_attr_bool(&nw, IFLA_GENEVE_COLLECT_METADATA, d != 0);
+
+ snl_end_attr_nested(&nw, off2);
+ snl_end_attr_nested(&nw, off);
+
+ geneve_nl_fini(ctx, &nw);
+}
+
+static struct cmd geneve_cmds[] = {
+
+ DEF_CLONE_CMD_ARG("genevemode", setgeneve_mode_clone),
+
+ DEF_CMD_ARG("geneveid", setgeneve_vni_nl),
+ DEF_CMD_ARG("genevelocal", setgeneve_local_nl),
+ DEF_CMD_ARG("geneveremote", setgeneve_remote_nl),
+ DEF_CMD_ARG("genevegroup", setgeneve_group_nl),
+ DEF_CMD_ARG("genevelocalport", setgeneve_local_port_nl),
+ DEF_CMD_ARG("geneveremoteport", setgeneve_remote_port_nl),
+ DEF_CMD_ARG2("geneveportrange", setgeneve_port_range_nl),
+ DEF_CMD_ARG("genevetimeout", setgeneve_timeout_nl),
+ DEF_CMD_ARG("genevemaxaddr", setgeneve_maxaddr_nl),
+ DEF_CMD_ARG("genevedev", setgeneve_dev_nl),
+ DEF_CMD_ARG("genevettl", setgeneve_ttl_nl),
+ DEF_CMD_ARG("genevedf", setgeneve_df_nl),
+ DEF_CMD("genevedscpinherit", 1, setgeneve_inherit_dscp_nl),
+ DEF_CMD("-genevedscpinherit", 0, setgeneve_inherit_dscp_nl),
+ DEF_CMD("genevelearn", 1, setgeneve_learn_nl),
+ DEF_CMD("-genevelearn", 0, setgeneve_learn_nl),
+ DEF_CMD("geneveflushall", 0, setgeneve_flush_nl),
+ DEF_CMD("geneveflush", 1, setgeneve_flush_nl),
+ DEF_CMD("geneveexternal", 1, setgeneve_external_nl),
+ DEF_CMD("-geneveexternal", 0, setgeneve_external_nl),
+
+ DEF_CMD_SARG("genevehwcsum", IFCAP2_GENEVE_HWCSUM_NAME,
+ setifcapnv),
+ DEF_CMD_SARG("-genevehwcsum", "-"IFCAP2_GENEVE_HWCSUM_NAME,
+ setifcapnv),
+ DEF_CMD_SARG("genevehwtso", IFCAP2_GENEVE_HWTSO_NAME,
+ setifcapnv),
+ DEF_CMD_SARG("-genevehwtso", "-"IFCAP2_GENEVE_HWTSO_NAME,
+ setifcapnv),
+};
+
+static struct afswtch af_geneve = {
+ .af_name = "af_geneve",
+ .af_af = AF_UNSPEC,
+ .af_other_status = geneve_status_nl,
+};
+
+static __constructor void
+geneve_ctor(void)
+{
+ size_t i;
+
+ for (i = 0; i < nitems(geneve_cmds); i++)
+ cmd_register(&geneve_cmds[i]);
+ af_register(&af_geneve);
+ clone_setdefcallback_prefix("geneve", geneve_create_nl);
+ SNL_VERIFY_PARSERS(all_parsers);
+}
diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c
index b51393ea750e..f6400684ba7a 100644
--- a/sbin/ifconfig/sfp.c
+++ b/sbin/ifconfig/sfp.c
@@ -77,7 +77,9 @@ sfp_status(if_ctx *ctx)
printf("\tvendor: %s PN: %s SN: %s DATE: %s\n",
vendor_info.name, vendor_info.pn, vendor_info.sn, vendor_info.date);
- if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) {
+ if (ifconfig_sfp_id_is_cmis(info.sfp_id)) {
+ /* CMIS: no legacy compliance info to show */
+ } else if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) {
if (verbose > 1)
printf("\tcompliance level: %s\n", strings.sfp_rev);
} else {
@@ -113,7 +115,17 @@ sfp_status(if_ctx *ctx)
if (ifconfig_sfp_get_sfp_dump(lifh, ctx->ifname, &dump) == -1)
return;
- if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) {
+ if (ifconfig_sfp_id_is_cmis(info.sfp_id)) {
+ printf("\n\tCMIS DUMP (Lower Memory 0..127):\n");
+ hexdump(dump.data, 128,
+ "\t", HD_OMIT_COUNT | HD_OMIT_CHARS);
+ printf("\n\tCMIS DUMP (Page 00h 128..255):\n");
+ hexdump(dump.data + 128, 128,
+ "\t", HD_OMIT_COUNT | HD_OMIT_CHARS);
+ printf("\n\tCMIS DUMP (Page 11h 128..255):\n");
+ hexdump(dump.data + CMIS_DUMP_P11, 128,
+ "\t", HD_OMIT_COUNT | HD_OMIT_CHARS);
+ } else if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) {
printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n");
hexdump(dump.data + QSFP_DUMP1_START, QSFP_DUMP1_SIZE,
"\t", HD_OMIT_COUNT | HD_OMIT_CHARS);
diff --git a/sbin/ipf/ippool/ippool.5 b/sbin/ipf/ippool/ippool.5
index b45675bea069..1ab8681bef8b 100644
--- a/sbin/ipf/ippool/ippool.5
+++ b/sbin/ipf/ippool/ippool.5
@@ -121,7 +121,7 @@ addresses from. To do this simply use a "file://" URL where you would
specify an actual IP address.
.PP
.nf
-pool ipf/tree (name rfc1918;) { "file:///etc/ipf/rfc1918;" };
+pool ipf/tree (name rfc1918;) { "file:///etc/ipf/rfc1918"; };
.fi
.PP
The contents of the file might look something like this:
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index a7bba4055b06..48e6a053a842 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -152,13 +152,13 @@ int pfctl_call_cleartables(int, int, struct pfr_anchoritem *);
int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *);
int pfctl_call_showtables(int, int, struct pfr_anchoritem *);
-RB_PROTOTYPE(pfctl_statelim_ids, pfctl_statelim, entry,
+RB_PROTOTYPE(pfctl_statelim_ids, pfctl_statelim, id_entry,
pfctl_statelim_id_cmp);
-RB_PROTOTYPE(pfctl_statelim_nms, pfctl_statelim, entry,
+RB_PROTOTYPE(pfctl_statelim_nms, pfctl_statelim, nm_entry,
pfctl_statelim_nm_cmp);
-RB_PROTOTYPE(pfctl_sourcelim_ids, pfctl_sourcelim, entry,
+RB_PROTOTYPE(pfctl_sourcelim_ids, pfctl_sourcelim, id_entry,
pfctl_sourcelim_id_cmp);
-RB_PROTOTYPE(pfctl_sourcelim_nms, pfctl_sourcelim, entry,
+RB_PROTOTYPE(pfctl_sourcelim_nms, pfctl_sourcelim, nm_entry,
pfctl_sourcelim_nm_cmp);
enum showopt_id {
@@ -4187,7 +4187,8 @@ pfctl_statelim_id_cmp(const struct pfctl_statelim *a,
return (0);
}
-RB_GENERATE(pfctl_statelim_ids, pfctl_statelim, entry, pfctl_statelim_id_cmp);
+RB_GENERATE(pfctl_statelim_ids, pfctl_statelim, id_entry,
+ pfctl_statelim_id_cmp);
static inline int
pfctl_statelim_nm_cmp(const struct pfctl_statelim *a,
@@ -4196,7 +4197,8 @@ pfctl_statelim_nm_cmp(const struct pfctl_statelim *a,
return (strcmp(a->ioc.name, b->ioc.name));
}
-RB_GENERATE(pfctl_statelim_nms, pfctl_statelim, entry, pfctl_statelim_nm_cmp);
+RB_GENERATE(pfctl_statelim_nms, pfctl_statelim, nm_entry,
+ pfctl_statelim_nm_cmp);
int
pfctl_add_statelim(struct pfctl *pf, struct pfctl_statelim *stlim)
@@ -4253,7 +4255,7 @@ pfctl_sourcelim_id_cmp(const struct pfctl_sourcelim *a,
return (0);
}
-RB_GENERATE(pfctl_sourcelim_ids, pfctl_sourcelim, entry,
+RB_GENERATE(pfctl_sourcelim_ids, pfctl_sourcelim, id_entry,
pfctl_sourcelim_id_cmp);
static inline int
@@ -4263,7 +4265,7 @@ pfctl_sourcelim_nm_cmp(const struct pfctl_sourcelim *a,
return (strcmp(a->ioc.name, b->ioc.name));
}
-RB_GENERATE(pfctl_sourcelim_nms, pfctl_sourcelim, entry,
+RB_GENERATE(pfctl_sourcelim_nms, pfctl_sourcelim, nm_entry,
pfctl_sourcelim_nm_cmp);
int
@@ -4272,8 +4274,9 @@ pfctl_add_sourcelim(struct pfctl *pf, struct pfctl_sourcelim *srlim)
struct pfctl_sourcelim *osrlim;
osrlim = RB_INSERT(pfctl_sourcelim_ids, &pf->sourcelim_ids, srlim);
- if (osrlim != NULL)
+ if (osrlim != NULL) {
return (-1);
+ }
osrlim = RB_INSERT(pfctl_sourcelim_nms, &pf->sourcelim_nms, srlim);
if (osrlim != NULL) {
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 8934238da148..631a6b9a32ea 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -77,7 +77,8 @@ struct pfr_buffer; /* forward definition */
struct pfctl_statelim {
struct pfctl_state_lim ioc;
- RB_ENTRY(pfctl_statelim) entry;
+ RB_ENTRY(pfctl_statelim) id_entry;
+ RB_ENTRY(pfctl_statelim) nm_entry;
};
RB_HEAD(pfctl_statelim_ids, pfctl_statelim);
@@ -85,7 +86,8 @@ RB_HEAD(pfctl_statelim_nms, pfctl_statelim);
struct pfctl_sourcelim {
struct pfctl_source_lim ioc;
- RB_ENTRY(pfctl_sourcelim) entry;
+ RB_ENTRY(pfctl_sourcelim) id_entry;
+ RB_ENTRY(pfctl_sourcelim) nm_entry;
};
RB_HEAD(pfctl_sourcelim_ids, pfctl_sourcelim);
diff --git a/sbin/ping/ping6.c b/sbin/ping/ping6.c
index 356f0f72a6f8..f81de062e59a 100644
--- a/sbin/ping/ping6.c
+++ b/sbin/ping/ping6.c
@@ -1083,21 +1083,21 @@ ping6(int argc, char *argv[])
#ifdef IPV6_RECVPKTINFO
if (setsockopt(srecv, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval,
sizeof(optval)) < 0)
- warn("setsockopt(IPV6_RECVPKTINFO)"); /* XXX err? */
+ err(1, "setsockopt(IPV6_RECVPKTINFO)");
#else /* old adv. API */
if (setsockopt(srecv, IPPROTO_IPV6, IPV6_PKTINFO, &optval,
sizeof(optval)) < 0)
- warn("setsockopt(IPV6_PKTINFO)"); /* XXX err? */
+ err(1, "setsockopt(IPV6_PKTINFO)");
#endif
#endif /* USE_SIN6_SCOPE_ID */
#ifdef IPV6_RECVHOPLIMIT
if (setsockopt(srecv, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval,
sizeof(optval)) < 0)
- warn("setsockopt(IPV6_RECVHOPLIMIT)"); /* XXX err? */
+ err(1, "setsockopt(IPV6_RECVHOPLIMIT)");
#else /* old adv. API */
if (setsockopt(srecv, IPPROTO_IPV6, IPV6_HOPLIMIT, &optval,
sizeof(optval)) < 0)
- warn("setsockopt(IPV6_HOPLIMIT)"); /* XXX err? */
+ err(1, "setsockopt(IPV6_HOPLIMIT)");
#endif
cap_rights_clear(&rights_srecv, CAP_SETSOCKOPT);
diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c
index 56160dafce28..f3e9306f8ca1 100644
--- a/sbin/tunefs/tunefs.c
+++ b/sbin/tunefs/tunefs.c
@@ -708,13 +708,13 @@ journal_findfile(void)
}
static void
-dir_clear_block(const dirblock *block, off_t off)
+dir_clear_block(dirblock *block, off_t off)
{
struct direct *dp;
for (; off < sblock.fs_bsize; off += DIRBLKSIZ) {
assert(off % alignof(struct direct) == 0);
- dp = (struct direct *)(uintptr_t)(block + off);
+ dp = (struct direct *)(uintptr_t)(block->buf + off);
dp->d_ino = 0;
dp->d_reclen = DIRBLKSIZ;
dp->d_type = DT_UNKNOWN;
diff --git a/secure/lib/libcrypto/Makefile b/secure/lib/libcrypto/Makefile
index 9d484e9d480c..f57f53a8c71f 100644
--- a/secure/lib/libcrypto/Makefile
+++ b/secure/lib/libcrypto/Makefile
@@ -635,7 +635,7 @@ INCS+= des.h dh.h dherr.h dsa.h
INCS+= dsaerr.h
INCS+= dtls1.h e_os2.h e_ostime.h ebcdic.h ec.h ecdh.h ecdsa.h ecerr.h encoder.h encodererr.h
INCS+= engine.h engineerr.h err.h ess.h esserr.h evp.h evperr.h fips_names.h fipskey.h hmac.h hpke.h http.h httperr.h idea.h indicator.h
-INCS+= kdf.h kdferr.h lhash.h macros.h md2.h md4.h md5.h mdc2.h modes.h obj_mac.h
+INCS+= kdf.h kdferr.h lhash.h macros.h md2.h md4.h md5.h mdc2.h ml_kem.h modes.h obj_mac.h
INCS+= objects.h objectserr.h ocsp.h ocsperr.h opensslconf.h opensslv.h
INCS+= ossl_typ.h param_build.h params.h pem.h pem2.h pemerr.h pkcs12.h pkcs12err.h pkcs7.h
INCS+= pkcs7err.h prov_ssl.h proverr.h provider.h quic.h rand.h randerr.h rc2.h rc4.h rc5.h ripemd.h
diff --git a/share/examples/Makefile b/share/examples/Makefile
index 0174792d2ecb..d977f2e5a0da 100644
--- a/share/examples/Makefile
+++ b/share/examples/Makefile
@@ -323,6 +323,7 @@ SE_DIRS+= sound
SE_SOUND= \
kqueue.c \
midi.c \
+ mmap.c \
oss.h \
poll.c \
select.c \
diff --git a/share/examples/sound/mmap.c b/share/examples/sound/mmap.c
new file mode 100644
index 000000000000..7f165d417020
--- /dev/null
+++ b/share/examples/sound/mmap.c
@@ -0,0 +1,297 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Goran Mekić
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program demonstrates low-latency audio pass-through using mmap.
+ * Opens input and output audio devices using memory-mapped I/O,
+ * synchronizes them in a sync group for simultaneous start,
+ * then continuously copies audio data from input to output.
+ */
+
+#include <time.h>
+
+#include "oss.h"
+
+/*
+ * Get current time in nanoseconds using monotonic clock.
+ * Monotonic clock is not affected by system time changes.
+ */
+static int64_t
+gettime_ns(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ err(1, "clock_gettime failed");
+ return ((int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec);
+}
+
+/*
+ * Sleep until the specified absolute time (in nanoseconds).
+ * Uses TIMER_ABSTIME for precise timing synchronization.
+ */
+static void
+sleep_until_ns(int64_t target_ns)
+{
+ struct timespec ts;
+
+ ts.tv_sec = target_ns / 1000000000LL;
+ ts.tv_nsec = target_ns % 1000000000LL;
+ if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) != 0)
+ err(1, "clock_nanosleep failed");
+}
+
+/*
+ * Calculate the number of frames to process per iteration.
+ * Higher sample rates require larger steps to maintain efficiency.
+ */
+static unsigned
+frame_stepping(unsigned sample_rate)
+{
+ return (16U * (1U + (sample_rate / 50000U)));
+}
+
+/*
+ * Update the mmap pointer and calculate progress.
+ * Returns the absolute progress in bytes.
+ *
+ * fd: file descriptor for the audio device
+ * request: ioctl request (SNDCTL_DSP_GETIPTR or SNDCTL_DSP_GETOPTR)
+ * map_pointer: current pointer position in the ring buffer
+ * map_progress: absolute progress in bytes
+ * buffer_bytes: total size of the ring buffer
+ * frag_size: size of each fragment
+ * frame_size: size of one audio frame in bytes
+ */
+static int64_t
+update_map_progress(int fd, unsigned long request, int *map_pointer,
+ int64_t *map_progress, int buffer_bytes, int frag_size, int frame_size)
+{
+ count_info info = {};
+ unsigned delta, max_bytes, cycles;
+ int fragments;
+
+ if (ioctl(fd, request, &info) < 0)
+ err(1, "Failed to get mmap pointer");
+ if (info.ptr < 0 || info.ptr >= buffer_bytes)
+ errx(1, "Pointer out of bounds: %d", info.ptr);
+ if ((info.ptr % frame_size) != 0)
+ errx(1, "Pointer %d not aligned to frame size %d", info.ptr,
+ frame_size);
+ if (info.blocks < 0)
+ errx(1, "Invalid block count %d", info.blocks);
+
+ /*
+ * Calculate delta: how many bytes have been processed since last check.
+ * Handle ring buffer wraparound using modulo arithmetic.
+ */
+ delta = (info.ptr + buffer_bytes - *map_pointer) % buffer_bytes;
+
+ /*
+ * Adjust delta based on reported blocks available.
+ * This accounts for cases where the pointer has wrapped multiple times.
+ */
+ max_bytes = (info.blocks + 1) * frag_size - 1;
+ if (max_bytes >= delta) {
+ cycles = max_bytes - delta;
+ cycles -= cycles % buffer_bytes;
+ delta += cycles;
+ }
+
+ /* Verify fragment count matches expected value */
+ fragments = delta / frag_size;
+ if (info.blocks < fragments || info.blocks > fragments + 1)
+ warnx("Pointer block mismatch: ptr=%d blocks=%d delta=%u",
+ info.ptr, info.blocks, delta);
+
+ /* Update pointer and progress tracking */
+ *map_pointer = info.ptr;
+ *map_progress += delta;
+ return (*map_progress);
+}
+
+/*
+ * Copy data between ring buffers, handling wraparound.
+ * The copy starts at 'offset' and copies 'length' bytes.
+ * If the copy crosses the buffer boundary, it wraps to the beginning.
+ */
+static void
+copy_ring(void *dstv, const void *srcv, int buffer_bytes, int offset,
+ int length)
+{
+ uint8_t *dst = dstv;
+ const uint8_t *src = srcv;
+ int first;
+
+ if (length <= 0)
+ return;
+
+ /* Calculate bytes to copy before wraparound */
+ first = buffer_bytes - offset;
+ if (first > length)
+ first = length;
+
+ /* Copy first part (up to buffer end or length) */
+ memcpy(dst + offset, src + offset, first);
+
+ /* Copy remaining part from beginning of buffer if needed */
+ if (first < length)
+ memcpy(dst, src, length - first);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch, bytes;
+ int frag_size, frame_size, verbose = 0;
+ int map_pointer = 0;
+ unsigned step_frames;
+ int64_t frame_ns, start_ns, next_wakeup_ns;
+ int64_t read_progress = 0, write_progress = 0;
+ oss_syncgroup sync_group = { 0, 0, { 0 } };
+ struct config config_in = {
+ .device = "/dev/dsp",
+ .mode = O_RDONLY | O_EXCL | O_NONBLOCK,
+ .format = AFMT_S32_NE,
+ .sample_rate = 48000,
+ .mmap = 1,
+ };
+ struct config config_out = {
+ .device = "/dev/dsp",
+ .mode = O_WRONLY | O_EXCL | O_NONBLOCK,
+ .format = AFMT_S32_NE,
+ .sample_rate = 48000,
+ .mmap = 1,
+ };
+
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch (ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!verbose)
+ printf("Use -v for verbose mode\n");
+
+ oss_init(&config_in);
+ oss_init(&config_out);
+
+ /*
+ * Verify input and output have matching ring-buffer geometry.
+ * The passthrough loop copies raw bytes at the same offset in both mmap
+ * buffers, so both devices must expose the same total byte count.
+ * They must also use the same max_channels because frame_size is
+ * derived from that value and all mmap pointers/lengths are expected to
+ * stay aligned to whole frames on both sides. If channels differed, the
+ * same byte offset could land in the middle of a frame on one device.
+ */
+ if (config_in.buffer_info.bytes != config_out.buffer_info.bytes)
+ errx(1,
+ "Input and output configurations have different buffer sizes");
+ if (config_in.audio_info.max_channels !=
+ config_out.audio_info.max_channels)
+ errx(1,
+ "Input and output configurations have different number of channels");
+
+ bytes = config_in.buffer_info.bytes;
+ frag_size = config_in.buffer_info.fragsize;
+ frame_size = config_in.sample_size * config_in.audio_info.max_channels;
+ if (frag_size != config_out.buffer_info.fragsize)
+ errx(1,
+ "Input and output configurations have different fragment sizes");
+
+ /* Calculate timing parameters */
+ step_frames = frame_stepping(config_in.sample_rate);
+ frame_ns = 1000000000LL / config_in.sample_rate;
+
+ /* Clear output buffer to prevent noise on startup */
+ memset(config_out.buf, 0, bytes);
+
+ /* Configure and start sync group */
+ sync_group.mode = PCM_ENABLE_INPUT;
+ if (ioctl(config_in.fd, SNDCTL_DSP_SYNCGROUP, &sync_group) < 0)
+ err(1, "Failed to add input to syncgroup");
+ sync_group.mode = PCM_ENABLE_OUTPUT;
+ if (ioctl(config_out.fd, SNDCTL_DSP_SYNCGROUP, &sync_group) < 0)
+ err(1, "Failed to add output to syncgroup");
+ if (ioctl(config_in.fd, SNDCTL_DSP_SYNCSTART, &sync_group.id) < 0)
+ err(1, "Starting sync group failed");
+
+ /* Initialize timing and progress tracking */
+ start_ns = gettime_ns();
+ read_progress = update_map_progress(config_in.fd, SNDCTL_DSP_GETIPTR,
+ &map_pointer, &read_progress, bytes, frag_size, frame_size);
+ write_progress = read_progress;
+ next_wakeup_ns = start_ns;
+
+ /*
+ * Main processing loop:
+ * 1. Sleep until next scheduled wakeup
+ * 2. Check how much new audio data is available
+ * 3. Copy available data from input to output buffer
+ * 4. Schedule next wakeup
+ */
+ for (;;) {
+ sleep_until_ns(next_wakeup_ns);
+ read_progress = update_map_progress(config_in.fd,
+ SNDCTL_DSP_GETIPTR, &map_pointer, &read_progress, bytes,
+ frag_size, frame_size);
+
+ /* Copy new audio data if available */
+ if (read_progress > write_progress) {
+ int offset = write_progress % bytes;
+ int length = read_progress - write_progress;
+
+ copy_ring(config_out.buf, config_in.buf, bytes, offset,
+ length);
+ write_progress = read_progress;
+ if (verbose)
+ printf("copied %d bytes at %d (abs %lld)\n",
+ length, offset, (long long)write_progress);
+ }
+
+ /* Schedule next wakeup based on frame timing */
+ next_wakeup_ns += (int64_t)step_frames * frame_ns;
+ if (next_wakeup_ns < gettime_ns())
+ next_wakeup_ns = gettime_ns();
+ }
+
+ if (munmap(config_in.buf, bytes) != 0)
+ err(1, "Memory unmap failed");
+ config_in.buf = NULL;
+ if (munmap(config_out.buf, bytes) != 0)
+ err(1, "Memory unmap failed");
+ config_out.buf = NULL;
+ close(config_in.fd);
+ close(config_out.fd);
+
+ return (0);
+}
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index c35859b30b60..60ab6e8bdab6 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -44,6 +44,7 @@ MAN= aac.4 \
alc.4 \
ale.4 \
alpm.4 \
+ appleir.4 \
altq.4 \
amdpm.4 \
${_amdsbwd.4} \
@@ -187,6 +188,7 @@ MAN= aac.4 \
gem.4 \
genet.4 \
genetlink.4 \
+ geneve.4 \
geom.4 \
geom_linux_lvm.4 \
geom_uzip.4 \
@@ -428,6 +430,7 @@ MAN= aac.4 \
ng_vjc.4 \
ng_vlan.4 \
ng_vlan_rotate.4 \
+ nlsysevent.4 \
nmdm.4 \
${_ntb.4} \
${_ntb_hw_amd.4} \
@@ -727,6 +730,7 @@ MLINKS+=fwip.4 if_fwip.4
MLINKS+=fxp.4 if_fxp.4
MLINKS+=gem.4 if_gem.4
MLINKS+=genet.4 if_genet.4
+MLINKS+=geneve.4 if_geneve.4
MLINKS+=geom.4 GEOM.4
MLINKS+=gif.4 if_gif.4
MLINKS+=gpio.4 gpiobus.4
diff --git a/share/man/man4/appleir.4 b/share/man/man4/appleir.4
new file mode 100644
index 000000000000..8717bf1b2e83
--- /dev/null
+++ b/share/man/man4/appleir.4
@@ -0,0 +1,93 @@
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd February 13, 2026
+.Dt APPLEIR 4
+.Os
+.Sh NAME
+.Nm appleir
+.Nd Apple IR receiver driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device appleir"
+.Cd "device hidbus"
+.Cd "device hid"
+.Cd "device evdev"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 Ns :
+.Bd -literal -offset indent
+appleir_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Apple IR receivers found in Mac computers
+(2006-2011 era).
+It supports both Apple Remote controls and generic IR remotes using the
+NEC infrared protocol.
+.Pp
+Supported devices include:
+.Bl -bullet -compact
+.It
+Apple IR Receiver (USB product IDs 0x8240, 0x8241, 0x8242, 0x8243, 0x1440)
+.El
+.Pp
+The driver decodes proprietary Apple Remote button presses and provides
+a default keymap for common NEC protocol codes used by generic IR remotes.
+Unmapped button codes can be accessed via the raw HID device at
+.Pa /dev/hidrawX
+for custom userland remapping.
+.Pp
+The
+.Pa /dev/input/eventX
+device presents the remote control as an
+evdev
+input device with standard KEY_* codes suitable for media applications.
+.Sh HARDWARE
+The
+.Nm
+driver supports Apple IR receivers with USB vendor ID 0x05ac and the
+following product IDs:
+.Pp
+.Bl -tag -width "0x8242" -compact
+.It 0x8240
+Apple IR Receiver (first generation)
+.It 0x8241
+Apple IR Receiver
+.It 0x8242
+Apple IR Receiver (Mac Mini 2011, MacBook Pro 3,1)
+.It 0x8243
+Apple IR Receiver
+.It 0x1440
+Apple IR Receiver (slim)
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/input/eventX" -compact
+.It Pa /dev/input/eventX
+evdev input device
+.It Pa /dev/hidrawX
+raw HID device for custom button mapping
+.El
+.Sh SEE ALSO
+evdev ,
+.Xr hidbus 4 ,
+.Xr usbhid 4
+.Pp
+NEC Infrared Transmission Protocol:
+.Lk https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt freebsd@seuros.com
+.Pp
+Based on protocol reverse-engineering by James McKenzie and others.
diff --git a/share/man/man4/arcmsr.4 b/share/man/man4/arcmsr.4
index 9356f4f23c8b..c2e7b4376ca7 100644
--- a/share/man/man4/arcmsr.4
+++ b/share/man/man4/arcmsr.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 15, 2020
+.Dd April 5, 2026
.Dt ARCMSR 4
.Os
.Sh NAME
@@ -66,7 +66,8 @@ Management tools for i386 and amd64 are available from Areca.
.Sh HARDWARE
The
.Nm
-driver supports the following cards:
+driver supports the following
+Areca PCI-X/PCIe SATA/SAS/NVMe RAID host adapters:
.Pp
.Bl -bullet -compact
.It
diff --git a/share/man/man4/asmc.4 b/share/man/man4/asmc.4
index 9e3550661797..9b42d021e1aa 100644
--- a/share/man/man4/asmc.4
+++ b/share/man/man4/asmc.4
@@ -112,6 +112,35 @@ minimum fan speed, the minimum speed and the maximum speed
respectively.
.Pp
All values are in RPM.
+.Sh RAW SMC KEY ACCESS
+When the kernel is compiled with the
+.Dv ASMC_DEBUG
+option, a set of sysctl nodes is provided under
+.Va dev.asmc.%d.raw
+for reading and writing arbitrary SMC keys by name.
+.Pp
+.Bl -tag -width "dev.asmc.%d.raw.value" -compact
+.It Va dev.asmc.%d.raw.key
+Set the 4-character SMC key name to access (e.g.,\&
+.Dq AUPO ) .
+Setting this automatically queries the key's length and type.
+.It Va dev.asmc.%d.raw.value
+Read or write the key's value as a hex string.
+.It Va dev.asmc.%d.raw.len
+The auto-detected value length in bytes (read-only).
+.It Va dev.asmc.%d.raw.type
+The 4-character SMC type string (e.g.,\&
+.Dq ui8 ,
+.Dq flt )
+(read-only).
+.El
+.Pp
+Example usage:
+.Bd -literal -offset indent
+sysctl dev.asmc.0.raw.key=AUPO
+sysctl dev.asmc.0.raw.value
+sysctl dev.asmc.0.raw.value=01
+.Ed
.Sh SUDDEN MOTION SENSOR
The Sudden Motion Sensor (SMS for short) is a device that detects
laptop movement and notifies the operating system via an interrupt.
diff --git a/share/man/man4/ciss.4 b/share/man/man4/ciss.4
index d731aaddad38..cf59836c47dd 100644
--- a/share/man/man4/ciss.4
+++ b/share/man/man4/ciss.4
@@ -1,7 +1,7 @@
.\" Written by Tom Rhodes
.\" This file is in the public domain.
.\"
-.Dd November 6, 2025
+.Dd April 6, 2026
.Dt CISS 4
.Os
.Sh NAME
@@ -116,6 +116,18 @@ HP Smart Array E200
.It
HP Smart Array E200i
.It
+HP Smart Array E500
+.It
+HP Smart Array H240
+.It
+HP Smart Array H240ar
+.It
+HP Smart Array H240nr
+.It
+HP Smart Array H241
+.It
+HP Smart Array H244br
+.It
HP Smart Array P212
.It
HP Smart Array P220i
@@ -124,6 +136,12 @@ HP Smart Array P222
.It
HP Smart Array P230i
.It
+HP Smart Array P240nr
+.It
+HP Smart Array P244br
+.It
+HP Smart Array P246br
+.It
HP Smart Array P400
.It
HP Smart Array P400i
@@ -146,30 +164,56 @@ HP Smart Array P430i
.It
HP Smart Array P431
.It
+HP Smart Array P440
+.It
HP Smart Array P440ar
.It
+HP Smart Array P441
+.It
HP Smart Array P530
.It
HP Smart Array P531
.It
+HP Smart Array P542d
+.It
HP Smart Array P600
.It
+HP Smart Array P700m
+.It
+HP Smart Array P712m
+.It
HP Smart Array P721m
.It
HP Smart Array P731m
.It
+HP Smart Array P741m
+.It
HP Smart Array P800
.It
HP Smart Array P812
.It
+HP Smart Array P822
+.It
HP Smart Array P830
.It
HP Smart Array P830i
.It
+HP Smart Array P840
+.It
+HP Smart Array P840ar
+.It
+HP Smart Array P841
+.It
HP Modular Smart Array 20 (MSA20)
.It
HP Modular Smart Array 500 (MSA500)
.El
+.Pp
+Additionally, several HP Smart Array controllers are supported by PCI
+subdevice ID only, as no model name is available for them:
+0x3220, 0x3222, 0x3230, 0x3231, 0x3232, 0x3233, 0x3236, 0x3238,
+0x3239, 0x323A, 0x323B, 0x323C, and 0x324B
+(all with PCI subvendor 0x103C).
.Sh SEE ALSO
.Xr cam 4 ,
.Xr pass 4 ,
diff --git a/share/man/man4/e6000sw.4 b/share/man/man4/e6000sw.4
index 92a3b5c8ed0b..be80888dc3b9 100644
--- a/share/man/man4/e6000sw.4
+++ b/share/man/man4/e6000sw.4
@@ -3,7 +3,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.Dd May 10, 2025
+.Dd April 16, 2026
.Dt E6000SW 4
.Sh NAME
.Nm e6000sw
@@ -31,6 +31,8 @@ Marvell 88E6176
.It
Marvell 88E6172
.It
+Marvell 88E6171
+.It
Marvell 88E6341
.It
Marvell 88E6141
diff --git a/share/man/man4/geneve.4 b/share/man/man4/geneve.4
new file mode 100644
index 000000000000..fdb752c0a8be
--- /dev/null
+++ b/share/man/man4/geneve.4
@@ -0,0 +1,384 @@
+.\"
+.\" Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd March 31, 2026
+.Dt GENEVE 4
+.Os
+.Sh NAME
+.Nm geneve
+.Nd Generic Network Virtualization Encapsulation interface
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Cd device geneve
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Cd if_geneve_load="YES"
+.Sh DESCRIPTION
+The
+.Nm
+driver creates a generic network virtualization tunnel interfaces
+for Tentant Systems over an L3 (IP/UDP) underlay network that provides
+a Layer 2 (ethernet) or Layer 3 service using
+.Nm
+protocol.
+.Pp
+This driver corresponds to RFC 8926 for format specification and by default
+uses the multicast-learning-based approach for its control plane.
+To provide control plane independence all of the driver-specific operations
+are implemented using
+.Xr rtnetlink 4
+and all the
+.Xr ioctl 2
+calls are implemented using the
+.Xr nv 9
+library.
+Each
+.Nm
+interface is created at runtime using interface cloning.
+This is most easily done with the
+.Xr ifconfig 8
+.Cm create
+command or using the
+.Va cloned_interfaces
+variable in
+.Xr rc.conf 5 .
+The interface may be removed with the
+.Xr ifconfig 8
+.Cm destroy
+command.
+.Pp
+The
+.Nm
+interface must be configured in either L2 or L3 mode.
+An L2
+.Nm
+tunnel could be used as a backplane between the virtual switches
+residing in hypervisors, switches, or other appliances.
+.Pp
+The L3
+.Nm
+tunnel provides virtualized IP forwarding service similar to IP/VRF.
+.Pp
+By default the
+.Nm
+driver creates an L2 interface that supports the usual network
+.Xr ioctl 2 Ns s
+and thus can be used with
+.Xr ifconfig 8
+like any other Ethernet interface.
+An L2
+.Nm
+interface encapsulates the Ethernet frame by prepending IP/UDP and
+.Nm
+headers.
+Thus, the encapsulated (inner) frame is able to be transmitted
+over a routed, Layer 3 network to the remote host.
+.Pp
+The
+.Nm
+interface may be configured in either unicast or multicast mode.
+When in unicast mode,
+the interface creates a tunnel to a single remote host,
+and all traffic is transmitted to that host.
+When in multicast mode,
+the interface joins an IP multicast group,
+and receives packets sent to the group address,
+and transmits packets to either the multicast group address,
+or directly to the remote host if there is an appropriate
+forwarding table entry.
+.Pp
+When the
+.Nm
+interface is brought up, a
+.Xr udp 4
+.Xr socket 9
+is created based on the configuration,
+such as the local address for unicast mode or
+the group address for multicast mode,
+and the listening (local) port number.
+Since multiple
+.Nm
+interfaces may be created that either
+use the same local address
+or join the same group address,
+and use the same port,
+the driver may share a socket among multiple interfaces.
+However, each interface within a socket must belong to
+a unique
+.Nm
+segment per
+.Xr vnet 9 .
+The analogous
+.Xr vlan 4
+configuration would be a physical interface configured as
+the parent device for multiple VLAN interfaces, each with
+a unique VLAN tag.
+Each
+.Nm
+segment is identified by a 24-bit value in the
+.Nm
+header called the
+.Dq Virtual Network Identifier ,
+or VNI.
+This value can be set with
+.Xr ifconfig 8
+.Cm geneveid
+parameter.
+.Pp
+When configured with the
+.Xr ifconfig 8
+.Cm genevelearn
+parameter, the interface dynamically creates forwarding table entries
+from received packets.
+An entry in the forwarding table maps the inner source MAC address
+to the outer remote IP address.
+During transmit, the interface attempts to lookup an entry for
+the encapsulated destination MAC address.
+If an entry is found, the IP address in the entry is used to directly
+transmit the encapsulated frame to the destination.
+Otherwise, when configured in multicast mode,
+the interface must flood the frame to all hosts in the group.
+The maximum number of entries in the table is configurable with the
+.Xr ifconfig 8
+.Cm genevemaxaddr
+command.
+Stale entries in the table are periodically pruned.
+The timeout is configurable with the
+.Xr ifconfig 8
+.Cm genevetimeout
+command.
+.Ss MTU
+Since the
+.Nm
+interface encapsulates the Ethernet frame with an IP, UDP, and
+.Nm
+header, the resulting frame may be larger than the MTU of the
+physical network.
+The
+.Nm
+specification recommends the physical network MTU be configured
+to use jumbo frames to accommodate the encapsulated frame size.
+.Pp
+By default, the
+.Nm
+driver sets its MTU to usual ethernet MTU of 1500 bytes, reduced by
+the size of geneve headers prepended which is depends on
+.Cm genevemode .
+.Pp
+Alternatively, the
+.Xr ifconfig 8
+.Cm mtu
+command may be used to set the fixed MTU size on the
+.Nm
+interface to allow the encapsulated frame to fit in the
+current MTU of the physical network.
+If the
+.Cm mtu
+command was used, system no longer adjust the
+.Nm
+interface MTU on routing or address changes.
+.Ss Hop Limit
+TTL value of
+.Nm
+interface can change by using the
+.Xr ifconfig 8
+.Cm genevettl
+command and it also can be inherited from carrying packet.
+You can set the
+.Cm genevettl
+to a number value or
+.Cm inherit
+option to be inherited at the encapsulation and decapsulation point.
+.Ss Traffic Class
+Just like the TTL value, ToS value can be inherited at the encapsulation point
+using
+.Xr ifconfig 8
+.Cm genevedscpinherit .
+As defined in RFC 8926, ECN value follows the RFC 6040 for both ingress and
+egress traffic.
+.Ss Don't Fragment
+To make sure fragmentation does not happing during transmission, you can
+set the
+.Xr ifconfig 8
+.Cm genevedf
+value to
+.Cm set
+value which sets the DF bit on IPv4 header and IP_DONTFRAG option on both IPv4
+and IPv6 sockets.
+Similar to other options, it can be set to
+.Cm inherit
+value.
+.Ss Multicast
+To create the
+.Nm
+interface with multicast underlay, one must use
+.Xr ifconfig 8
+.Cm genevegroup
+instead of
+.Cm geneveremote
+and set it to a multicast address (e.g. ff08::db8:0:1, 239.0.0.1).
+One can set the outbound multicast interface with
+.Xr ifconfig 8
+.Cm genevedev
+to bound its multicast group to specific interface.
+.Pp
+The
+.Cm ip_mroute
+kernel module for IPv4 underlay and
+.Cm ip6_mroute
+for IPv6 underlay must be loaded for
+.Xr multicast 4
+to function.
+.Sh HARDWARE
+The
+.Nm
+driver supports hardware checksum offload (receive and transmit) and TSO on the
+encapsulated traffic over physical interfaces that support these features.
+The
+.Nm
+interface examines the
+.Cm genevedev
+interface, if one is specified, or the interface hosting the
+.Cm genevelocal
+address, and configures its capabilities based on the hardware offload
+capabilities of that physical interface.
+If multiple physical interfaces will transmit or receive traffic for the
+.Nm
+then they all must have the same hardware capabilities.
+The transmit routine of a
+.Nm
+interface may fail with
+.Er ENXIO
+if an outbound physical interface does not support
+an offload that the
+.Nm
+interface is requesting.
+This can happen if there are multiple physical interfaces involved, with
+different hardware capabilities, or an interface capability was disabled after
+the
+.Nm
+interface had already started.
+.Sh EXAMPLES
+.Bd -literal
+ Host A (198.51.100.10)
+ +--------------------+
+ | VNI 100 10.1.1.0/24|
+ | VNI 200 10.2.2.0/24|
+ +---------+----------+
+ |
+ (198.51.100.0/24)
+ |
+ +---------------v---------------+
+ | Host B (203.0.113.1) |
+ | +------+-------+ |
+ | geneve0| |geneve1|
+ | +------v----+ +-----v-----+ |
+ | | bridge0 | | bridge1 | |
+ | | (VNI 100) | | (VNI 200) | |
+ | +------+----+ +----+------+ |
+ | | | |
+ +--------v-------------v--------+
+ epair0b| |epair1b
+ +------+----+ +----+------+
+ | Jail A | | Jail B |
+ | (10.1.1.x)| | (10.2.2.x)|
+ +-----------+ +-----------+
+.Ed
+Assume host A has the (external) IP address 198.51.100.10 and
+two internal addresses of 10.1.1.1/24 and 10.2.2.1/24, while
+host B has the external address of 203.0.113.10 and two jails
+with their own separate
+.Xr VNET 9 .
+the following commands will configure the tunnel:
+.Pp
+On host A, create a l2
+.Nm
+interface in unicast mode:
+.Bd -literal
+ifconfig geneve0 create geneveid 100 genevelocal 198.51.100.10 geneveremote 203.0.113.1
+ifconfig geneve1 create geneveid 200 genevelocal 198.51.100.10 geneveremote 203.0.113.1
+.Ed
+.Pp
+On host B:
+.Bd -literal
+ifconfig geneve0 create geneveid 100 genevelocal 203.0.113.1 geneveremote 198.51.100.10
+ifconfig geneve1 create geneveid 200 genevelocal 203.0.113.1 geneveremote 198.51.100.10
+ifconfig bridge0 addm geneve0 addm epair0a
+ifconfig bridge1 addm geneve1 addm epair1a
+.Ed
+.Pp
+The example below demonstrate multicast configuration with IPv6:
+.Bd -literal
+ ----------- VNI 42 -----------
+ / \\
+2001:db8::1/64 --- Host A ------ Multicast ------- Host B --- 2001:db8::2/64
+ 3fff::1 [em0] ff08::db8:1 [em0] 3fff::2
+.Ed
+.Pp
+Create a
+.Nm
+interface in multicast mode,
+with the
+.Cm genevelocal
+address of 3fff::1,
+and the
+.Cm genevegroup
+address of ff08::db8:0:1.
+The em0 interface will be used to transmit multicast packets.
+On host A:
+.Bd -literal
+ifconfig geneve0 create geneveid 42 genevelocal 3fff::1 genevegroup ff08::db8:1 genevedev em0
+.Ed
+.Pp
+On host B:
+.Bd -literal
+ifconfig geneve0 create geneveid 42 genevelocal 3fff::2 genevegroup ff08::db8:1 genevedev em0
+.Ed
+.Pp
+Once created, the
+.Nm
+interface can be configured with
+.Xr ifconfig 8 .
+.Pp
+The following when placed in the file
+.Pa /etc/rc.conf
+will cause a geneve interface called
+.Dq Li geneve0
+to be created, and will configure the interface in unicast mode.
+.Bd -literal
+cloned_interfaces="geneve0"
+create_args_geneve0="geneveid 108 genevelocal 192.168.100.1 geneveremote 192.168.100.2"
+.Ed
+.Sh SEE ALSO
+.Xr inet 4 ,
+.Xr inet6 4 ,
+.Xr multicast 4 ,
+.Xr rtnetlink 4 ,
+.Xr vlan 4 ,
+.Xr rc.conf 5 ,
+.Xr ifconfig 8 ,
+.Xr sysctl 8
+.Rs
+.%A "J. Gross, Ed."
+.%A "I. Gross, Ed."
+.%A "T. Sridhar, Ed."
+.%T "Geneve: Generic Network Virtualization Encapsulation"
+.%D November 2020
+.%O "RFC 8926"
+.Re
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Seyed Pouria Mousavizadeh Tehrani Aq info@spmzt.net
+.Sh BUGS
+Current geneve implementation with netlink can't set geneve options
+other than genevemode during interface cloning in ifconfig without
+specifying the interface index.
diff --git a/share/man/man4/ix.4 b/share/man/man4/ix.4
index 09af85f5c4a7..39ed49aa8dfc 100644
--- a/share/man/man4/ix.4
+++ b/share/man/man4/ix.4
@@ -138,7 +138,7 @@ The
driver supports the following
.Xr sysctl 8
variables:
-.Bl -tag -width "dev.ix.?.debug.dump.clusters"
+.Bl -tag -width "dev.ix.?.debug.fw_log.severity.<module>"
.It Va dev.ix.?.debug.dump.clusters
Specify a bitmask to select firmware event clusters
to be included in the debug dump.
@@ -160,6 +160,38 @@ Output must be redirected to a file
and decoded by Intel Customer Support.
.Pp
This feature is only supported on E610.
+.It Va dev.ix.?.debug.fw_log.severity.<module>
+Specify firmware logging verbosity level for the specified module.
+Available levels include:
+.Pp
+.Bl -tag -compact
+.It 0
+none
+.It 1
+error
+.It 2
+warning
+.It 3
+normal
+.It 4
+verbose
+.El
+.Pp
+Supported modules: general, ctrl, link, link_topo, dnl, i2c, sdp, mdio,
+adminq, hdma, lldp, dcbx, dcb, xlr, nvm, auth, vpd, iosf, parser, sw,
+scheduler, txq, acl, post, watchdog, task_dispatch, mng, synce, health,
+tsdrv, pfreg, mdlver.
+.Pp
+This feature is only supported on E610 devices.
+.It Va dev.ix.?.debug.fw_log.register
+Specify 1 to apply per-device firmware logging configuration.
+.Pp
+This feature is only supported on E610 devices.
+.It Va dev.ix.?.debug.fw_log.on_load
+Enable firmware logging during driver initialization when set via
+.Xr kenv 1 .
+.Pp
+This feature is only supported on E610 devices.
.El
.Sh DIAGNOSTICS
.Bl -diag
diff --git a/share/man/man4/nlsysevent.4 b/share/man/man4/nlsysevent.4
new file mode 100644
index 000000000000..3b46ab65f568
--- /dev/null
+++ b/share/man/man4/nlsysevent.4
@@ -0,0 +1,132 @@
+.\"
+.\" Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd April 9, 2026
+.Dt NLSYSEVENT 4
+.Os
+.Sh NAME
+.Nm nlsysevent
+.Nd Netlink-based kernel event notification module
+.Sh SYNOPSIS
+To load the driver as a module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+nlsysevent_load="YES"
+.Ed
+.Pp
+Alternatively, to load the module at runtime:
+.Bd -literal -offset indent
+kldload nlsysevent
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+kernel module provides a
+.Xr netlink 4
+Generic Netlink interface for receiving kernel device events.
+It hooks into the
+.Xr devctl 4
+notification system and re-publishes all kernel events as Generic Netlink
+multicast messages under the
+.Dv NETLINK_GENERIC
+protocol family.
+.Pp
+This provides an alternative to reading events from
+.Pa /dev/devctl
+.Pq used by Xr devd 8 ,
+with the advantage that multiple consumers can subscribe to events
+simultaneously, and consumers can selectively subscribe to specific
+event groups.
+.Ss Generic Netlink Family
+The module registers a Generic Netlink family named
+.Dq Li nlsysevent .
+The dynamically-assigned family identifier can be resolved using the
+standard
+.Dv CTRL_CMD_GETFAMILY
+request as described in
+.Xr genetlink 4 .
+.Ss Commands
+The following command is defined:
+.Bl -tag -width indent
+.It Dv NLSE_CMD_NEWEVENT
+Sent when a kernel event occurs.
+This message is never solicited by userland; it is only delivered to
+sockets that have subscribed to one or more multicast groups.
+.El
+.Ss Attributes
+Each
+.Dv NLSE_CMD_NEWEVENT
+message contains the following TLV attributes:
+.Bl -tag -width indent
+.It Dv NLSE_ATTR_SYSTEM
+(string) The system name that generated the event
+.Pq e.g., Dq Li ACPI , Dq Li IFNET , Dq Li USB .
+.It Dv NLSE_ATTR_SUBSYSTEM
+(string) The subsystem name within the system.
+.It Dv NLSE_ATTR_TYPE
+(string) The type of the event.
+.It Dv NLSE_ATTR_DATA
+(string) Optional extra data associated with the event.
+This attribute may be absent if no extra data was provided.
+.El
+.Ss Multicast Groups
+The module creates one multicast group per system name.
+The following groups are pre-registered when the module is loaded:
+.Pp
+.Bl -column "HYPERV_NIC_VF" -offset indent -compact
+.It Li ACPI
+.It Li AEON
+.It Li CAM
+.It Li CARP
+.It Li coretemp
+.It Li DEVFS
+.It Li device
+.It Li ETHERNET
+.It Li GEOM
+.It Li HYPERV_NIC_VF
+.It Li IFNET
+.It Li INFINIBAND
+.It Li KERNEL
+.It Li nvme
+.It Li PMU
+.It Li RCTL
+.It Li USB
+.It Li VFS
+.It Li VT
+.It Li ZFS
+.El
+.Pp
+Additional groups are created dynamically as new system names appear in
+kernel events, up to a maximum of 64 groups.
+.Pp
+The group identifier for a given system name can be resolved via
+.Dv CTRL_CMD_GETFAMILY
+and then subscribed to using the
+.Dv NETLINK_ADD_MEMBERSHIP
+socket option.
+.Sh EXAMPLES
+The
+.Xr genl 1
+utility can be used to monitor events:
+.Bd -literal -offset indent
+genl monitor nlsysevent
+.Ed
+.Sh SEE ALSO
+.Xr genl 1 ,
+.Xr snl 3 ,
+.Xr devctl 4 ,
+.Xr genetlink 4 ,
+.Xr netlink 4 ,
+.Xr devd 8
+.Sh HISTORY
+The
+.Nm
+module first appeared in
+.Fx 15.0 .
+.Sh AUTHORS
+The
+.Nm
+kernel module and this manual page were written by
+.An Baptiste Daroussin Aq Mt bapt@FreeBSD.org .
diff --git a/share/man/man4/rge.4 b/share/man/man4/rge.4
index 2b781e287e3c..24c42f9106dc 100644
--- a/share/man/man4/rge.4
+++ b/share/man/man4/rge.4
@@ -3,7 +3,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.Dd December 18, 2025
+.Dd April 5, 2026
.Dt RGE 4
.Os
.Sh NAME
@@ -39,8 +39,8 @@ over CAT6 cable.
.Pp
All NICs supported by the
.Nm
-driver have TCP/IP checksum offload and hardware VLAN tagging/insertion
-features, and use a descriptor-based DMA mechanism.
+driver have TCP/IP checksum offload, hardware VLAN tagging/insertion
+features, Wake On Lan (WOL), and use a descriptor-based DMA mechanism.
They are also
capable of TCP large send (TCP segmentation offload).
.Pp
@@ -152,6 +152,15 @@ Maximum number of RX packets to process per interrupt.
The default value is 16.
Increasing this value may improve throughput on high-speed links at the
cost of increased interrupt latency.
+.It Va dev.rge.%d.disable_aspm
+Disable PCIe Active State Power Management (ASPM) and Extended
+Configuration Power Management (ECPM).
+The default value is 0 (leave ASPM enabled).
+Setting this to 1 reduces latency at the cost of increased power
+consumption.
+This tunable can only be set in
+.Xr loader.conf 5
+and requires a reboot to take effect.
.El
.Sh DIAGNOSTICS
.Bl -diag
diff --git a/share/man/man5/rc.conf.5 b/share/man/man5/rc.conf.5
index fa8d8aab8c4e..b666345def48 100644
--- a/share/man/man5/rc.conf.5
+++ b/share/man/man5/rc.conf.5
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd November 14, 2025
+.Dd April 2, 2026
.Dt RC.CONF 5
.Os
.Sh NAME
@@ -4950,6 +4950,60 @@ For instance, the file
is created to prevent
.Xr shutdown 8
targeted at the wrong machine.
+.It Va virtual_oss_enable
+.Pq Vt bool
+If set to
+.Dq Li YES ,
+run one
+.Xr virtual_oss 8
+instance for each configuration defined in
+.Pa virtual_oss_configs .
+.It Va virtual_oss_configs
+.Pq Vt str
+Space-separated list of
+.Xr virtual_oss 8
+configurations.
+For example:
+.Bd -literal
+virtual_oss_configs="foo bar"
+.Ed
+.Pp
+Configurations need to be defined in
+.Pa virtual_oss_ Ns Aq Ar config_name .
+By default, there is a
+.Pa dsp
+configuration which replaces the
+.Pa /dev/dsp
+device created by
+.Xr sound 4
+with a
+.Xr virtual_oss 8
+one.
+It can be redefined by setting the
+.Pa virtual_oss_dsp
+variable.
+.It Va virtual_oss_ Ns Aq Ar config_name
+.Pq Vt str
+.Xr virtual_oss 8
+argument list for configuration
+.Aq Ar config_name .
+.It Va virtual_oss_default_control_device
+.Pq Vt str
+The
+.Xr virtual_oss 8
+control device's name corresponding to the default configuration,
+.Pa virtual_oss_dsp .
+This is set by default to
+.Pa vdsp.ctl .
+When
+.Pa virtual_oss_dsp
+is set, it is strongly encouraged to set this variable as well, and use it as
+the
+.Fl t
+option's argument in
+.Pa virtual_oss_dsp ,
+because it is used by other programs and scripts, such as
+.Pa /etc/devd/snd.conf .
.El
.Sh SERVICE JAILS
The service jails part of the rc system automatically puts a service
@@ -5170,6 +5224,7 @@ to
.Xr unbound 8 ,
.Xr usbconfig 8 ,
.Xr utx 8 ,
+.Xr virtual_oss 8 ,
.Xr wlandebug 8 ,
.Xr yp 8 ,
.Xr ypbind 8 ,
diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5
index a94c5d6177b4..65fbcf2e69a4 100644
--- a/share/man/man5/src.conf.5
+++ b/share/man/man5/src.conf.5
@@ -1,5 +1,5 @@
.\" DO NOT EDIT-- this file is @generated by tools/build/options/makeman.
-.Dd April 3, 2026
+.Dd April 10, 2026
.Dt SRC.CONF 5
.Os
.Sh NAME
@@ -721,8 +721,6 @@ An alternate bootstrap tool chain must be provided.
.It Va WITHOUT_EXAMPLES
Avoid installing examples to
.Pa /usr/share/examples/ .
-.It Va WITH_EXPERIMENTAL
-Include experimental features in the build.
.It Va WITHOUT_FDT
Do not build Flattened Device Tree support as part of the base system.
This includes the device tree compiler (dtc) and libfdt support library.
diff --git a/share/man/man7/freebsd-base.7 b/share/man/man7/freebsd-base.7
index 383808579487..c690a7a9e0b9 100644
--- a/share/man/man7/freebsd-base.7
+++ b/share/man/man7/freebsd-base.7
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd December 4, 2025
+.Dd April 1, 2026
.Dt FREEBSD-BASE 7
.Os
.Sh NAME
@@ -183,13 +183,14 @@ The system test suite, installed in
All available system kernels.
.El
.Sh EXAMPLES
+.Ss Install a single piece of userland
Install the
.Xr vi 1
text editor on the running system:
.Bd -literal -offset indent
pkg install FreeBSD-vi
.Ed
-.Pp
+.Ss Install userland to a jail
Install a new
.Xr jail 8
system using the
@@ -198,17 +199,17 @@ package set:
.Bd -literal -offset indent
pkg -r /jails/myjail install FreeBSD-set-minimal-jail
.Ed
-.Pp
+.Ss Install native compilers
Install C/C++ compilers on the running system:
.Bd -literal -offset indent
pkg install FreeBSD-set-devel
.Ed
-.Pp
+.Ss Update the currently running system
Apply available updates to the running system:
.Bd -literal -offset indent
pkg upgrade -r FreeBSD-base
.Ed
-.Pp
+.Ss Install cross compilers
Install the development toolchain for FreeBSD/powerpc64le in an
alternate root (for example, to support cross-compiling software
for a different target than the host system):
@@ -216,6 +217,39 @@ for a different target than the host system):
pkg -r /ppcdev -oABI=FreeBSD:16:powerpc64le \e
install FreeBSD-set-devel
.Ed
+.Ss Unregister a currently running system
+Systems managed through
+.Xr pkg 8
+can be unregistered from the package manager \(em
+for example to upgrade in-place via
+.Dq make installworld .
+See
+.Xr build 7 .
+.Pp
+To unregister the base system from the package manager:
+.Bd -literal -offset indent
+pkg unregister -fg 'FreeBSD-\e*'
+.Ed
+.Pp
+Then, disable the base system package repository.
+If a configuration file was created in
+.Pa /usr/local/etc/pkg/repos/
+to enable base system packages, remove it:
+.Bd -literal -offset indent
+rm /usr/local/etc/pkg/repos/FreeBSD-base.conf
+.Ed
+.Pp
+Alternatively, if it is desired to keep it,
+edit the file and change
+.Dq Li enabled:
+to
+.Dq Li no
+to disable the entry.
+.Pp
+.Sy Warning :
+This is a destructive action
+which will prevent updating the base system via
+.Xr pkg 8 .
.Sh SEE ALSO
.Xr build 7 ,
.Xr pkg 8 ,
@@ -224,3 +258,4 @@ pkg -r /ppcdev -oABI=FreeBSD:16:powerpc64le \e
Support for installing the base system as packages was introduced in
.Fx 15.0 .
Earlier releases supported a subset of this functionality.
+Support for unregistering an existing installation appeared in pkg 2.5.
diff --git a/share/man/man7/tuning.7 b/share/man/man7/tuning.7
index 586b63c247a8..831362e9e179 100644
--- a/share/man/man7/tuning.7
+++ b/share/man/man7/tuning.7
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 28, 2025
+.Dd April 9, 2026
.Dt TUNING 7
.Os
.Sh NAME
@@ -687,7 +687,7 @@ There are many solutions to saturated disks:
increasing memory for caching, mirroring disks, distributing operations across
several machines, and so forth.
.Pp
-Finally, you might run out of network suds.
+Finally, you might run out of network resources.
Optimize the network path
as much as possible.
For example, in
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index 31a3f886d0f3..29c822c10eb2 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -174,6 +174,7 @@ MAN= accept_filter.9 \
gone_in.9 \
hardclock.9 \
hash.9 \
+ hashalloc.9 \
hashinit.9 \
hexdump.9 \
hhook.9 \
@@ -1216,6 +1217,7 @@ MLINKS+=hash.9 hash32.9 \
hash.9 hash32_strne.9 \
hash.9 jenkins_hash.9 \
hash.9 jenkins_hash32.9
+MLINKS+=hashalloc.9 hashfree.9
MLINKS+=hashinit.9 hashdestroy.9 \
hashinit.9 hashinit_flags.9 \
hashinit.9 phashinit.9
diff --git a/share/man/man9/OF_getprop.9 b/share/man/man9/OF_getprop.9
index 3bb0068e3dc2..8c93d92bf73b 100644
--- a/share/man/man9/OF_getprop.9
+++ b/share/man/man9/OF_getprop.9
@@ -23,7 +23,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd June 23, 2018
+.Dd April 16, 2026
.Dt OF_GETPROP 9
.Os
.Sh NAME
@@ -52,7 +52,7 @@
.Ft ssize_t
.Fn OF_getencprop "phandle_t node" "const char *prop" \
"pcell_t *buf" "size_t len"
-.Ft int
+.Ft bool
.Fn OF_hasprop "phandle_t node" "const char *propname"
.Ft ssize_t
.Fn OF_searchprop "phandle_t node" "const char *propname" \
@@ -138,11 +138,15 @@ if the property does not exist.
must be a multiple of 4.
.Pp
.Fn OF_hasprop
-returns 1 if the device node
+returns
+.Dv true
+if the device node
.Fa node
has a property specified by
.Fa propname ,
-and zero if the property does not exist.
+and
+.Dv false
+if the property does not exist.
.Pp
.Fn OF_searchprop
recursively looks for the property specified by
diff --git a/share/man/man9/hashalloc.9 b/share/man/man9/hashalloc.9
new file mode 100644
index 000000000000..c68444bf05ed
--- /dev/null
+++ b/share/man/man9/hashalloc.9
@@ -0,0 +1,314 @@
+.\"
+.\" Copyright (c) 2026 Gleb Smirnoff <glebius@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 12, 2026
+.Dt HASHALLOC 9
+.Os
+.Sh NAME
+.Nm hashalloc ,
+.Nm hashfree
+.Nd allocate and free kernel hash tables
+.Sh SYNOPSIS
+.In sys/malloc.h
+.In sys/hash.h
+.Ft void *
+.Fn hashalloc "struct hashalloc_args *args"
+.Ft void
+.Fn hashfree "void *table" "struct hashalloc_args *args"
+.Sh DESCRIPTION
+The
+.Fn hashalloc
+and
+.Fn hashfree
+functions provide a flexible kernel programming interface (KPI) for allocating
+and freeing hash tables with configurable bucket headers.
+.Pp
+.Pp
+.Fn hashalloc
+allocates memory for a hash table according to the parameters
+specified in the
+.Fa args
+structure.
+It computes an appropriate number of buckets (adjusting
+.Fa args->size
+as needed based on the requested
+.Fa type ) ,
+allocates memory using
+.Xr malloc 9 ,
+initializes each bucket's queue header (e.g.,
+.Vt LIST_HEAD ,
+.Vt TAILQ_HEAD ,
+etc.), and, if requested, initializes per-bucket locks.
+The returned memory allocation can be used as an array of header structures
+that start with initialized list header of the requested type followed by
+initialized lock of requested type.
+.Pp
+.Fn hashfree
+frees a hash table previously allocated by
+.Fn hashalloc .
+.Pp
+Both functions require the caller to pass the same (or equivalent)
+.Fa struct hashalloc_args
+that specifies the desired configuration of the hash table and has the
+following members:
+.Bd -literal -offset indent
+struct hashalloc_args {
+ /* Required arguments */
+ size_t size; /* in: desired buckets, out: allocated */
+ int mflags; /* malloc(9) flags */
+ struct malloc_type *mtype; /* malloc(9) type */
+ /* Optional arguments */
+ size_t hdrsize; /* bucket header size; 0 = auto */
+ enum {
+ HASH_TYPE_POWER2,
+ HASH_TYPE_PRIME,
+ } type; /* default HASH_TYPE_POWER2 */
+ enum {
+ HASH_HEAD_LIST,
+ HASH_HEAD_CK_LIST,
+ HASH_HEAD_SLIST,
+ HASH_HEAD_CK_SLIST,
+ HASH_HEAD_STAILQ,
+ HASH_HEAD_CK_STAILQ,
+ HASH_HEAD_TAILQ,
+ } head; /* default HASH_HEAD_LIST */
+ enum {
+ HASH_LOCK_NONE,
+ HASH_LOCK_MTX,
+ HASH_LOCK_RWLOCK,
+ HASH_LOCK_SX,
+ HASH_LOCK_RMLOCK,
+ HASH_LOCK_RMSLOCK,
+ } lock; /* default HASH_LOCK_NONE */
+ int lopts; /* lock init options */
+ const char *lname; /* lock name */
+ int (*ctor)(void *); /* bucket constructor */
+ void (*dtor)(void *); /* bucket destructor */
+ /* Returned arguments */
+ int error; /* error code in case of failure */
+};
+.Ed
+.Pp
+Argument members
+.Fa size ,
+.Fa mflags
+and
+.Fa mtype
+are required for the
+.Fn hashalloc .
+The argument
+.Fa size ,
+as filled by earlier call to
+.Fn hashalloc ,
+and
+.Fa mtype
+are required for the
+.Fn hashfree .
+The rest of arguments are optional and have reasonable defaults.
+A hash table that was allocated with a non-default allocation arguments shall
+pass the same arguments to
+.Fn hashfree .
+The structure shall be initialized with sparse C99 initializer as it may
+contain opaque extension members.
+The structure may be allocated on the stack of a caller.
+.Bl -tag -width ".Fa hdrsize"
+.It Fa size
+Desired number of buckets for
+.Fn hashalloc .
+Upon a successful return
+.Fn hashalloc
+sets this member to the actual number allocated
+(may be rounded up to power-of-2 or nearest prime).
+The value returned by
+.Fn hashalloc
+shall be later supplied to the
+.Fn hashfree .
+.It Fa mflags , Fa mtype
+Passed directly to
+.Xr malloc 9 .
+.It Fa hdrsize
+Optional member that allows the caller to set a different (increased) size
+of a bucket header.
+.It Fa type
+Bucket count policy:
+.Bl -tag -width ".Dv HASH_TYPE_POWER2"
+.It Dv HASH_TYPE_POWER2
+Rounded up to largest power of two less than or equal to argument
+.Fa size .
+.It Dv HASH_TYPE_PRIME
+Sized to the largest prime number less than or equal to argument
+.Fa size .
+.El
+.Pp
+Default is
+.Dv HASH_TYPE_POWER2 .
+.It Fa head
+Queue header type for each bucket, a
+.Xr queue 3
+or
+Concurrency-kit (CK) type.
+.Bl -tag -width ".Dv HASH_HEAD_CK_STAILQ"
+.It Dv HASH_HEAD_LIST
+.Xr queue 3
+.Fd LIST_HEAD
+.It Dv HASH_HEAD_CK_LIST
+Concurrency-kit
+.Fd CK_LIST_HEAD
+.It Dv HASH_HEAD_SLIST
+.Xr queue 3
+.Fd SLIST_HEAD
+.It Dv HASH_HEAD_CK_SLIST
+Concurrency-kit
+.Fd CK_SLIST_HEAD
+.It Dv HASH_HEAD_STAILQ
+.Xr queue 3
+.Fd STAILQ_HEAD
+.It Dv HASH_HEAD_CK_STAILQ
+Concurrency-kit
+.Fd CK_STAILQ_HEAD
+.It Dv HASH_HEAD_TAILQ
+.Xr queue 3
+.Fd TAILQ_HEAD
+.El
+.Pp
+Default is
+.Dv HASH_HEAD_LIST .
+.It Fa lock
+Synchronization:
+.Bl -tag -width ".Dv HASH_LOCK_RWLOCK"
+.It Dv HASH_LOCK_NONE
+No per-bucket lock.
+.It Dv HASH_LOCK_MTX
+Per-bucket
+.Xr mutex 9 .
+.It Dv HASH_LOCK_RWLOCK
+Per-bucket
+.Xr rwlock 9 .
+.It Dv HASH_LOCK_SX
+Per-bucket
+.Xr sx 9 .
+.It Dv HASH_LOCK_RMLOCK
+Per-bucket
+.Xr rmlock 9 .
+.It Dv HASH_LOCK_RMSLOCK
+Per-bucket sleepable (rms)
+.Xr rmlock 9 .
+.El
+.Pp
+Default is
+.Dv HASH_LOCK_NONE .
+.It Fa lopts
+Options passed to
+.Xr mtx_init 9 ,
+.Xr rw_init 9 ,
+.Xr sx_init 9 ,
+.Xr rm_init 9
+or
+.Xr rms_init 9
+(if locking is enabled).
+.It Fa lname
+Lock name.
+This member is required unless
+.Fa lock
+is
+.Dv HASH_LOCK_NONE .
+.It Fa ctor
+Optional constructor to be called by
+.Fn hashalloc
+for each bucket after list header and lock initialization.
+May fail with error code, yielding in a failure of
+.Fn hashalloc .
+.It Fa dtor
+Optional destructor to be called by
+.Fn hashfree
+for each bucket before lock destructors and list emptyness checks.
+.El
+.Sh RETURN VALUES
+.Fn hashalloc
+returns a pointer to the allocated and initialized hash table on success, or
+.Dv NULL
+on memory allocation failure or constructor failure.
+The
+.Fa error
+member of
+.Fa args
+is set to appropriate error code.
+When
+.Fa mflags
+in
+.Fa args
+contain the
+.Va M_WAITOK
+flag and the
+.Fa ctor
+is either NULL or never fails, then
+.Fn hashalloc
+never fails.
+.Sh EXAMPLES
+A simple mutex-protected hash table using TAILQ buckets:
+.Bd -literal -offset indent
+struct bucket {
+ TAILQ_HEAD(, foo) head;
+ struct mtx lock;
+} *table;
+
+struct hashalloc_args args = {
+ .size = 9000,
+ .mflags = M_WAITOK,
+ .mtype = M_FOO,
+ .head = HASH_HEAD_TAILQ,
+ .lock = HASH_LOCK_MTX,
+ .lopts = MTX_DEF,
+ .lname = "bucket of foo",
+};
+
+table = hashalloc(&args);
+/* Use table as array of struct bucket ... */
+mtx_lock(&table[hash].lock);
+TAILQ_INSERT_HEAD(&table[hash].head, foo, next);
+
+/* Later */
+hashfree(table, &args);
+.Ed
+.Sh SEE ALSO
+.Xr malloc 9 ,
+.Xr mutex 9 ,
+.Xr rmlock 9 ,
+.Xr rwlock 9 ,
+.Xr sx 9 ,
+.Xr queue 3
+.Sh HISTORY
+The
+.Nm
+KPI first appeared in
+.Fx 16.0 .
+It supersedes older interface
+.Fn hashinit ,
+that was available since
+.Bx 4.4 ,
+by offering greater control over the hash table structure and locking
+strategy.
+.Sh AUTHORS
+.An Gleb Smirnoff Aq Mt glebius@FreeBSD.org
diff --git a/share/man/man9/hashinit.9 b/share/man/man9/hashinit.9
index 7d2f75d58d03..8c6a3888efe8 100644
--- a/share/man/man9/hashinit.9
+++ b/share/man/man9/hashinit.9
@@ -23,7 +23,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd April 29, 2016
+.Dd March 17, 2026
.Dt HASHINIT 9
.Os
.Sh NAME
@@ -44,6 +44,12 @@
.Ft "void *"
.Fn phashinit "int nelements" "struct malloc_type *type" "u_long *nentries"
.Fn phashinit_flags "int nelements" "struct malloc_type *type" "u_long *nentries" "int flags"
+.Sh WARNING
+This KPI is obsolete and scheduled for removal in
+.Fx 17 .
+Use
+.Xr hashalloc 9
+instead.
.Sh DESCRIPTION
The
.Fn hashinit ,
@@ -178,6 +184,7 @@ pointed to by
.Fa hashtbl
is not empty.
.Sh SEE ALSO
+.Xr hashalloc 9 ,
.Xr LIST_HEAD 3 ,
.Xr malloc 9
.Sh BUGS
diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
index 5c3e3bb31a7f..ab774d44c283 100644
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -206,7 +206,6 @@ __DEFAULT_NO_OPTIONS = \
DISK_IMAGE_TOOLS_BOOTSTRAP \
DTRACE_ASAN \
DTRACE_TESTS \
- EXPERIMENTAL \
HESIOD \
IPFILTER_IPFS \
LOADER_VERBOSE \
diff --git a/share/skel/dot.profile b/share/skel/dot.profile
index 40dfa58f4e84..d67a6e2a934b 100644
--- a/share/skel/dot.profile
+++ b/share/skel/dot.profile
@@ -1,5 +1,5 @@
#
-# .profile - Bourne Shell startup script for login shells
+# .profile - POSIX Shell startup script for login shells
#
# see also sh(1), environ(7).
#
diff --git a/share/vt/keymaps/Makefile b/share/vt/keymaps/Makefile
index 117eadde9fac..27291f7709fd 100644
--- a/share/vt/keymaps/Makefile
+++ b/share/vt/keymaps/Makefile
@@ -8,6 +8,7 @@ FILES= INDEX.keymaps \
bg.phonetic.kbd \
br.kbd \
br.noacc.kbd \
+ br.lenovo.kbd \
by.kbd \
ca.kbd \
ca-fr.kbd \
@@ -79,6 +80,7 @@ FILES= INDEX.keymaps \
uk.kbd \
uk.macbook.kbd \
us.acc.kbd \
+ us.intl.acc.kbd \
us.ctrl.kbd \
us.dvorak.kbd \
us.dvorakl.kbd \
diff --git a/stand/common/commands.c b/stand/common/commands.c
index 41687ece42fd..4ed247a6b935 100644
--- a/stand/common/commands.c
+++ b/stand/common/commands.c
@@ -308,7 +308,7 @@ command_set(int argc, char *argv[])
ves = ve_status_get(-1);
if (ves == VE_UNVERIFIED_OK) {
#ifdef LOADER_VERIEXEC_TESTING
- printf("Checking: %s\n", var);
+ printf("Checking: %s\n", argv[1]);
#endif
if (is_restricted_var(argv[1])) {
printf("Ignoring restricted variable: %s\n",
diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
index b9f55a21e403..10131f7ccb88 100644
--- a/stand/common/load_elf.c
+++ b/stand/common/load_elf.c
@@ -283,7 +283,8 @@ __elfN(load_elf_header)(char *filename, elf_file_t ef)
{
int verror;
- ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__);
+ ef->vctx = vectx_open(ef->fd, filename, VE_MUST,
+ 0L, NULL, &verror, __func__);
if (verror) {
printf("Unverified %s: %s\n", filename, ve_error_get());
close(ef->fd);
@@ -504,7 +505,7 @@ out:
if (!err && ef.vctx) {
int verror;
- verror = vectx_close(ef.vctx, VE_MUST, __func__);
+ verror = vectx_close(ef.vctx, __func__);
if (verror) {
err = EAUTH;
file_discard(fp);
@@ -1095,7 +1096,7 @@ out:
if (!err && ef.vctx) {
int verror;
- verror = vectx_close(ef.vctx, VE_MUST, __func__);
+ verror = vectx_close(ef.vctx, __func__);
if (verror) {
err = EAUTH;
file_discard(fp);
diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c
index 9e32daa53696..706391ffbd8f 100644
--- a/stand/common/load_elf_obj.c
+++ b/stand/common/load_elf_obj.c
@@ -104,7 +104,8 @@ __elfN(obj_loadfile)(char *filename, uint64_t dest,
{
int verror;
- ef.vctx = vectx_open(ef.fd, filename, 0L, NULL, &verror, __func__);
+ ef.vctx = vectx_open(ef.fd, filename, VE_MUST,
+ 0L, NULL, &verror, __func__);
if (verror) {
printf("Unverified %s: %s\n", filename, ve_error_get());
close(ef.fd);
@@ -196,7 +197,7 @@ out:
if (!err && ef.vctx) {
int verror;
- verror = vectx_close(ef.vctx, VE_MUST, __func__);
+ verror = vectx_close(ef.vctx, __func__);
if (verror) {
err = EAUTH;
file_discard(fp);
diff --git a/stand/common/module.c b/stand/common/module.c
index bc06ba01fa06..f75428458373 100644
--- a/stand/common/module.c
+++ b/stand/common/module.c
@@ -661,6 +661,7 @@ file_loadraw(const char *fname, const char *type, int insert)
vm_offset_t laddr;
#ifdef LOADER_VERIEXEC_VECTX
struct vectx *vctx;
+ int severity;
int verror;
#endif
@@ -690,7 +691,16 @@ file_loadraw(const char *fname, const char *type, int insert)
}
#ifdef LOADER_VERIEXEC_VECTX
- vctx = vectx_open(fd, name, 0L, NULL, &verror, __func__);
+ severity = severity_guess(name);
+ if (severity < VE_MUST) {
+ /* double check against type */
+ if (strcmp(type, "md_image") == 0
+ || strcmp(type, "mfs_root") == 0
+ || strcmp(type, "acpi_dsdt") == 0
+ || strcmp(type, "cpu_microcode") == 0)
+ severity = VE_MUST;
+ }
+ vctx = vectx_open(fd, name, severity, 0L, NULL, &verror, __func__);
if (verror) {
sprintf(command_errbuf, "can't verify '%s': %s",
name, ve_error_get());
@@ -741,7 +751,9 @@ file_loadraw(const char *fname, const char *type, int insert)
if (module_verbose > MODULE_VERBOSE_SILENT)
printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr));
#ifdef LOADER_VERIEXEC_VECTX
- verror = vectx_close(vctx, VE_MUST, __func__);
+ verror = vectx_close(vctx, __func__);
+ DEBUG_PRINTF(1,("%s: vectx_close(%s): %d\n", __func__,
+ name, verror));
if (verror) {
free(name);
close(fd);
diff --git a/stand/efi/loader/arch/amd64/trap.c b/stand/efi/loader/arch/amd64/trap.c
index 3fe86f7b1924..ff7190d214ad 100644
--- a/stand/efi/loader/arch/amd64/trap.c
+++ b/stand/efi/loader/arch/amd64/trap.c
@@ -82,14 +82,68 @@ struct frame {
};
void report_exc(struct trapframe *tf);
-void
-report_exc(struct trapframe *tf)
+
+static void
+stack_trace(struct frame *fp, uintptr_t pc)
{
- struct frame *fp;
- uintptr_t pc, base;
+ uintptr_t base;
char buf[80];
base = (uintptr_t)boot_img->ImageBase;
+
+ printf("Stack trace:\n");
+ pager_open();
+ while (fp != NULL || pc != 0) {
+ struct frame *nfp;
+ char *source = "PC";
+
+ if (pc >= base && pc < base + boot_img->ImageSize) {
+ pc -= base;
+ source = "loader PC";
+ }
+ (void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",
+ (uintptr_t)fp, source, pc);
+ if (pager_output(buf))
+ break;
+
+ if (fp == NULL)
+ break;
+
+ nfp = fp->fr_savfp;
+ if (nfp != NULL && nfp <= fp) {
+ printf("FP %016lx: loop detected, stopping trace\n",
+ (uintptr_t)nfp);
+ break;
+ }
+ fp = nfp;
+
+ if (fp != NULL)
+ pc = fp->fr_savpc;
+ else
+ pc = 0;
+ }
+ pager_close();
+}
+
+void
+panic_action(void)
+{
+ struct frame *fp;
+ uintptr_t rip;
+
+ __asm __volatile("movq %%rbp,%0" : "=r" (fp));
+ rip = fp->fr_savpc;
+
+ stack_trace(fp, rip);
+ printf("--> Press a key on the console to reboot <--\n");
+ getchar();
+ printf("Rebooting...\n");
+ exit(1);
+}
+
+void
+report_exc(struct trapframe *tf)
+{
/*
* printf() depends on loader runtime and UEFI firmware health
* to produce the console output, in case of exception, the
@@ -116,32 +170,8 @@ report_exc(struct trapframe *tf)
tf->tf_r9, tf->tf_rax, tf->tf_rbx, tf->tf_rbp, tf->tf_r10,
tf->tf_r11, tf->tf_r12, tf->tf_r13, tf->tf_r14, tf->tf_r15);
- fp = (struct frame *)tf->tf_rbp;
- pc = tf->tf_rip;
-
- printf("Stack trace:\n");
- pager_open();
- while (fp != NULL || pc != 0) {
- char *source = "PC";
-
- if (pc >= base && pc < base + boot_img->ImageSize) {
- pc -= base;
- source = "loader PC";
- }
- (void) snprintf(buf, sizeof (buf), "FP %016lx: %s 0x%016lx\n",
- (uintptr_t)fp, source, pc);
- if (pager_output(buf))
- break;
+ stack_trace((struct frame *)tf->tf_rbp, tf->tf_rip);
- if (fp != NULL)
- fp = fp->fr_savfp;
-
- if (fp != NULL)
- pc = fp->fr_savpc;
- else
- pc = 0;
- }
- pager_close();
printf("Machine stopped.\n");
}
diff --git a/stand/efi/loader/arch/arm/exec.c b/stand/efi/loader/arch/arm/exec.c
index 50e94af05fd2..a33855397a5a 100644
--- a/stand/efi/loader/arch/arm/exec.c
+++ b/stand/efi/loader/arch/arm/exec.c
@@ -69,9 +69,6 @@ __elfN(arm_exec)(struct preloaded_file *fp)
efi_time_fini();
- entry = efi_translate(e->e_entry);
-
- printf("Kernel entry at %p...\n", entry);
printf("Kernel args: %s\n", fp->f_args);
/*
@@ -85,6 +82,8 @@ __elfN(arm_exec)(struct preloaded_file *fp)
return (error);
}
+ entry = efi_translate(e->e_entry);
+
(*entry)((void *)modulep);
panic("exec returned");
}
diff --git a/stand/efi/loader/arch/arm64/exec.c b/stand/efi/loader/arch/arm64/exec.c
index 6b5181b54507..406be822a28c 100644
--- a/stand/efi/loader/arch/arm64/exec.c
+++ b/stand/efi/loader/arch/arm64/exec.c
@@ -67,7 +67,6 @@ elf64_exec(struct preloaded_file *fp)
return(EFTYPE);
ehdr = (Elf_Ehdr *)&(md->md_data);
- entry = efi_translate(ehdr->e_entry);
/*
* we have to cleanup here because net_cleanup() doesn't work after
@@ -82,6 +81,8 @@ elf64_exec(struct preloaded_file *fp)
return (err);
}
+ entry = efi_translate(ehdr->e_entry);
+
/* Clean D-cache under kernel area and invalidate whole I-cache */
clean_addr = (vm_offset_t)efi_translate(fp->f_addr);
clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr;
diff --git a/stand/efi/loader/arch/riscv/exec.c b/stand/efi/loader/arch/riscv/exec.c
index 64e522775471..900e8ca8ff62 100644
--- a/stand/efi/loader/arch/riscv/exec.c
+++ b/stand/efi/loader/arch/riscv/exec.c
@@ -85,9 +85,6 @@ __elfN(exec)(struct preloaded_file *fp)
efi_time_fini();
- entry = efi_translate(e->e_entry);
-
- printf("Kernel entry at %p...\n", entry);
printf("Kernel args: %s\n", fp->f_args);
/*
@@ -101,6 +98,8 @@ __elfN(exec)(struct preloaded_file *fp)
return (error);
}
+ entry = efi_translate(e->e_entry);
+
(*entry)((void *)modulep);
panic("exec returned");
}
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
index e54f3e1f9f35..9604f16a2480 100644
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -1007,10 +1007,10 @@ parse_uefi_con_out(void)
* If we don't have any Con* variable use both. If we have GOP
* make video primary, otherwise set serial primary. In either
* case, try to use both the 'efi' console which will use the
- * GOP, if present and serial. If there's an EFI BIOS that omits
- * this, but has a serial port redirect, we'll unavioidably get
- * doubled characters, but we'll be right in all the other more
- * common cases.
+ * GOP, if present and serial. If there's a UEFI firmware that
+ * omit this, but has a serial port redirect, we'll unavoidably
+ * get doubled characters, but we'll be right in all the other
+ * more common cases.
*/
if (efi_has_gop())
how |= RB_MULTIPLE;
diff --git a/stand/i386/gptzfsboot/zfsboot.c b/stand/i386/gptzfsboot/zfsboot.c
index 4c8eae9b65e5..72d791ad2364 100644
--- a/stand/i386/gptzfsboot/zfsboot.c
+++ b/stand/i386/gptzfsboot/zfsboot.c
@@ -320,10 +320,12 @@ main(void)
else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
putchar('\n');
auto_boot = 0;
- if (parse_cmd())
+ if (parse_cmd()) {
putchar('\a');
- else
+ } else {
+ putchar('\n');
load();
+ }
}
}
diff --git a/stand/i386/loader/chain.c b/stand/i386/loader/chain.c
index 9d58f9f3de33..5d8d66039770 100644
--- a/stand/i386/loader/chain.c
+++ b/stand/i386/loader/chain.c
@@ -83,7 +83,7 @@ command_chain(int argc, char *argv[])
}
#ifdef LOADER_VERIEXEC_VECTX
- vctx = vectx_open(fd, argv[1], 0L, NULL, &verror, __func__);
+ vctx = vectx_open(fd, argv[1], VE_MUST, 0L, NULL, &verror, __func__);
if (verror) {
sprintf(command_errbuf, "can't verify: %s", argv[1]);
close(fd);
@@ -127,7 +127,7 @@ command_chain(int argc, char *argv[])
}
close(fd);
#ifdef LOADER_VERIEXEC_VECTX
- verror = vectx_close(vctx, VE_MUST, __func__);
+ verror = vectx_close(vctx, __func__);
if (verror) {
free(vctx);
return (CMD_ERROR);
diff --git a/stand/libofw/openfirm.c b/stand/libofw/openfirm.c
index 1df65784e47a..ce4444262f6d 100644
--- a/stand/libofw/openfirm.c
+++ b/stand/libofw/openfirm.c
@@ -288,6 +288,13 @@ OF_getencprop(phandle_t package, const char *propname, cell_t *buf, int buflen)
return (retval);
}
+/* Check existence of a property of a package. */
+bool
+OF_hasprop(phandle_t node, const char *prop)
+{
+ return (OF_getproplen(node, prop) >= 0);
+}
+
/* Get the next property of a package. */
int
OF_nextprop(phandle_t package, const char *previous, char *buf)
diff --git a/stand/libofw/openfirm.h b/stand/libofw/openfirm.h
index 35d10c320b57..b4890762b0f9 100644
--- a/stand/libofw/openfirm.h
+++ b/stand/libofw/openfirm.h
@@ -89,6 +89,7 @@ phandle_t OF_instance_to_package(ihandle_t);
int OF_getproplen(phandle_t, const char *);
int OF_getprop(phandle_t, const char *, void *, int);
int OF_getencprop(phandle_t, const char *, cell_t *, int);
+bool OF_hasprop(phandle_t, const char *);
int OF_nextprop(phandle_t, const char *, char *);
int OF_setprop(phandle_t, const char *, void *, int);
int OF_canon(const char *, char *, int);
diff --git a/stand/man/loader.efi.8 b/stand/man/loader.efi.8
index 80d16d45670a..8bb88dd2e24f 100644
--- a/stand/man/loader.efi.8
+++ b/stand/man/loader.efi.8
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 3, 2024
+.Dd April 11, 2026
.Dt LOADER.EFI 8
.Os
.Sh NAME
@@ -64,7 +64,7 @@ process.
.Xr boot1.efi 8
is deprecated for new installations.
.Ss Console Considerations
-The EFI BIOS provides a generic console.
+The UEFI firmware provides a generic console.
In
.Nm
this is selected by specifying
@@ -114,8 +114,8 @@ primary if a UEFI graphics device is detected, or the serial console
as primary if not.
.Pp
On x86 platforms, if you wish to redirect the loader's output to a serial port
-when the EFI BIOS doesn't support it, or to a serial port that isn't the one the
-EFI BIOS redirects its output to, set
+when the UEFI firmware doesn't support it, or to a serial port that isn't the
+one the UEFI firmware redirects its output to, set
.Dv console
to
.Dq comconsole .
@@ -135,9 +135,9 @@ If this causes a doubling of characters, set
.Dv console
to
.Dq efi ,
-since your EFI BIOS is redirecting to the serial port already.
+since your UEFI firmware is redirecting to the serial port already.
.Pp
-If your EFI BIOS redirects the serial port, you may need to tell the kernel
+If your UEFI firmware redirects the serial port, you may need to tell the kernel
which address to use.
EFI uses ACPI's UID to identify the serial port, but
.Nm
diff --git a/stand/powerpc/boot1.chrp/boot1.c b/stand/powerpc/boot1.chrp/boot1.c
index 1a546f3473e2..1bb06bf82983 100644
--- a/stand/powerpc/boot1.chrp/boot1.c
+++ b/stand/powerpc/boot1.chrp/boot1.c
@@ -102,14 +102,11 @@ ofwh_t bootdevh;
ofwh_t stdinh, stdouth;
/*
- * Note about the entry point:
+ * Our entrypoint.
*
- * For some odd reason, the first page of the load appears to have trouble
- * when entering in LE. The first five instructions decode weirdly.
- * I suspect it is some cache weirdness between the ELF headers and .text.
- *
- * Ensure we have a gap between the start of .text and the entry as a
- * workaround.
+ * A bug in the SLOF shipped with some versions of QEMU causes the first
+ * 32 bytes of .text to be wrongly byte-swapped when loading LE programs.
+ * As a workaround, we add some padding at the start of the text section.
*/
__asm(" \n\
.data \n\
@@ -118,7 +115,7 @@ stack: \n\
.space 16384 \n\
\n\
.text \n\
- /* SLOF cache hack */ \n\
+ /* SLOF workaround */ \n\
.space 4096 \n\
.globl _start \n\
_start: \n\
diff --git a/stand/powerpc/ofw/ofwfdt.c b/stand/powerpc/ofw/ofwfdt.c
index eeb5f132a3f2..5422924a3b4c 100644
--- a/stand/powerpc/ofw/ofwfdt.c
+++ b/stand/powerpc/ofw/ofwfdt.c
@@ -32,12 +32,6 @@
extern int command_fdt_internal(int argc, char *argv[]);
-static int
-OF_hasprop(phandle_t node, const char *prop)
-{
- return (OF_getproplen(node, (char *)prop) > 0);
-}
-
static void
add_node_to_fdt(void *buffer, phandle_t node, int fdt_offset)
{
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 7de19f86afbf..c6a095f2d98a 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -229,6 +229,9 @@ device ppi # Parallel port interface device
device puc # Multi I/O cards and multi-channel UARTs
+# MDIO bus for Ethernet NICs that directly expose the MDIO bus
+device mdio
+
# PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure
device iflib
device em # Intel PRO/1000 Gigabit Ethernet Family
diff --git a/sys/arm/broadcom/bcm2835/bcm2838_xhci.c b/sys/arm/broadcom/bcm2835/bcm2838_xhci.c
index 25579d7227a5..b1461d17d391 100644
--- a/sys/arm/broadcom/bcm2835/bcm2838_xhci.c
+++ b/sys/arm/broadcom/bcm2835/bcm2838_xhci.c
@@ -89,7 +89,7 @@ bcm_xhci_probe(device_t dev)
root = OF_finddevice("/");
if (root == -1)
return (ENXIO);
- if (!ofw_bus_node_is_compatible(root, "raspberrypi,4-model-b"))
+ if (!ofw_bus_node_is_compatible(root, "brcm,bcm2711"))
return (ENXIO);
/*
@@ -105,7 +105,7 @@ bcm_xhci_probe(device_t dev)
return (ENXIO);
device_set_desc(dev,
- "VL805 USB 3.0 controller (on the Raspberry Pi 4b)");
+ "VL805 USB 3.0 controller (on the Raspberry Pi 4 series)");
return (BUS_PROBE_SPECIFIC);
}
diff --git a/sys/arm/broadcom/bcm2835/raspberrypi_virtgpio.c b/sys/arm/broadcom/bcm2835/raspberrypi_virtgpio.c
index 64ce6fda6306..797896a7f22b 100644
--- a/sys/arm/broadcom/bcm2835/raspberrypi_virtgpio.c
+++ b/sys/arm/broadcom/bcm2835/raspberrypi_virtgpio.c
@@ -206,14 +206,14 @@ rpi_virt_gpio_probe(device_t dev)
union msg_gpiovirtbuf cfg;
int rv;
- if (ofw_bus_status_okay(dev) == 0)
+ if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
gpio = ofw_bus_get_node(dev);
- if (OF_hasprop(gpio, "gpio-controller") == 0)
+ if (!OF_hasprop(gpio, "gpio-controller"))
return (ENXIO);
/* Check whether the firmware is ready. */
diff --git a/sys/arm/qualcomm/std.ipq4018 b/sys/arm/qualcomm/std.ipq4018
index 89d2cb546bf3..1c70ecc448bd 100644
--- a/sys/arm/qualcomm/std.ipq4018
+++ b/sys/arm/qualcomm/std.ipq4018
@@ -39,6 +39,8 @@ dev/qcom_gcc/qcom_gcc_clock.c optional qcom_gcc
dev/qcom_gcc/qcom_gcc_reset.c optional qcom_gcc
dev/qcom_gcc/qcom_gcc_ipq4018_reset.c optional qcom_gcc
dev/qcom_gcc/qcom_gcc_ipq4018_clock.c optional qcom_gcc
+dev/qcom_gcc/qcom_gcc_msm8916_reset.c optional qcom_gcc
+dev/qcom_gcc/qcom_gcc_msm8916_clock.c optional qcom_gcc
dev/qcom_clk/qcom_clk_fepll.c optional qcom_gcc
dev/qcom_clk/qcom_clk_fdiv.c optional qcom_gcc
diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c
index 4b5361090ead..400ea5860695 100644
--- a/sys/arm64/arm64/identcpu.c
+++ b/sys/arm64/arm64/identcpu.c
@@ -3086,10 +3086,10 @@ print_cpu_cache(struct cpu_desc *desc, struct sbuf *sb, uint64_t ccs,
* register.
*/
if ((desc->id_aa64mmfr2 & ID_AA64MMFR2_CCIDX_64))
- cache_size = (CCSIDR_NSETS_64(ccs) + 1) *
- (CCSIDR_ASSOC_64(ccs) + 1);
+ cache_size = (CCSIDR_NumSets64(ccs) + 1) *
+ (CCSIDR_Assoc64(ccs) + 1);
else
- cache_size = (CCSIDR_NSETS(ccs) + 1) * (CCSIDR_ASSOC(ccs) + 1);
+ cache_size = (CCSIDR_NumSets(ccs) + 1) * (CCSIDR_Assoc(ccs) + 1);
cache_size *= line_size;
sbuf_printf(sb, "%zuKB (%s)", cache_size / 1024,
@@ -3377,7 +3377,7 @@ identify_cpu(u_int cpu)
int j = 0;
if ((clidr & CLIDR_CTYPE_IO)) {
WRITE_SPECIALREG(csselr_el1,
- CSSELR_Level(i) | CSSELR_InD);
+ CSSELR_Level(i) | CSSELR_InD_IC);
desc->ccsidr[i][j++] =
READ_SPECIALREG(ccsidr_el1);
}
diff --git a/sys/arm64/arm64/locore.S b/sys/arm64/arm64/locore.S
index f1228235dfe7..c86f98da55a8 100644
--- a/sys/arm64/arm64/locore.S
+++ b/sys/arm64/arm64/locore.S
@@ -398,7 +398,7 @@ LENTRY(enter_kernel_el)
*/
/* Configure the Hypervisor */
- ldr x2, =(HCR_RW | HCR_APK | HCR_API)
+ ldr x2, =(HCR_RW | HCR_APK | HCR_API | HCR_ATA)
msr hcr_el2, x2
/* Stash value of HCR_EL2 for later */
@@ -1063,6 +1063,21 @@ LENTRY(start_mmu)
isb
ldr x2, mair
+
+ /*
+ * If MTE is supported, configure GCR_EL1 and clear the TFSR registers of
+ * any pending tag check faults
+ */
+ CHECK_CPU_FEAT(x3, ID_AA64PFR1, MTE, MTE, 1f)
+
+ /* Set GCR_EL1, non-zero tags excluded by default */
+ mov x3, #(GCR_Exclude_MASK | GCR_RRND)
+ msr GCR_EL1_REG, x3
+
+ /* Clear any pending tag check faults */
+ msr TFSR_EL1_REG, xzr
+ msr TFSRE0_EL1_REG, xzr
+1:
msr mair_el1, x2
/*
@@ -1119,7 +1134,8 @@ mair:
MAIR_ATTR(MAIR_NORMAL_NC, VM_MEMATTR_UNCACHEABLE) | \
MAIR_ATTR(MAIR_NORMAL_WB, VM_MEMATTR_WRITE_BACK) | \
MAIR_ATTR(MAIR_NORMAL_WT, VM_MEMATTR_WRITE_THROUGH) | \
- MAIR_ATTR(MAIR_DEVICE_nGnRE, VM_MEMATTR_DEVICE_nGnRE)
+ MAIR_ATTR(MAIR_DEVICE_nGnRE, VM_MEMATTR_DEVICE_nGnRE) | \
+ MAIR_ATTR(MAIR_NORMAL_TG, VM_MEMATTR_TAGGED)
tcr:
#if PAGE_SIZE == PAGE_SIZE_4K
#define TCR_TG (TCR_TG1_4K | TCR_TG0_4K)
diff --git a/sys/arm64/arm64/pmap.c b/sys/arm64/arm64/pmap.c
index 678030f827dd..5017b5f63c4d 100644
--- a/sys/arm64/arm64/pmap.c
+++ b/sys/arm64/arm64/pmap.c
@@ -290,6 +290,10 @@ VM_PAGE_TO_PV_LIST_LOCK(vm_page_t m)
#define PTE_TO_VM_PAGE(pte) PHYS_TO_VM_PAGE(PTE_TO_PHYS(pte))
#define VM_PAGE_TO_PTE(m) PHYS_TO_PTE(VM_PAGE_TO_PHYS(m))
+static struct mtx cmap_lock;
+static void *cmap1_addr;
+static pt_entry_t *cmap1_pte;
+
/*
* The presence of this flag indicates that the mapping is writeable.
* If the ATTR_S1_AP_RO bit is also set, then the mapping is clean, otherwise
@@ -1363,7 +1367,7 @@ pmap_bootstrap(void)
virtual_avail = preinit_map_va + PMAP_PREINIT_MAPPING_SIZE;
virtual_avail = roundup2(virtual_avail, L1_SIZE);
- virtual_end = VM_MAX_KERNEL_ADDRESS - PMAP_MAPDEV_EARLY_SIZE;
+ virtual_end = VM_MAX_KERNEL_ADDRESS - PMAP_MAPDEV_EARLY_SIZE - L2_SIZE;
kernel_vm_end = virtual_avail;
/*
@@ -1419,6 +1423,19 @@ pmap_bootstrap(void)
alloc_pages(msgbufpv, round_page(msgbufsize) / PAGE_SIZE);
msgbufp = (void *)msgbufpv;
+ /* Allocate space for the CPU0 CMAP */
+ bs_state.va = virtual_end;
+ pmap_bootstrap_l2_table(&bs_state);
+ pmap_store(&bs_state.l3[pmap_l3_index(bs_state.va)],
+ PHYS_TO_PTE(pmap_early_vtophys((vm_offset_t)bs_state.l3)) |
+ ATTR_AF | pmap_sh_attr | ATTR_S1_XN | ATTR_KERN_GP |
+ ATTR_S1_IDX(VM_MEMATTR_WRITE_BACK) | L3_PAGE);
+ dsb(ishst);
+
+ mtx_init(&cmap_lock, "SYSMAPS", NULL, MTX_DEF);
+ cmap1_addr = (void *)(virtual_end + L3_SIZE);
+ cmap1_pte = &bs_state.l3[pmap_l3_index((vm_offset_t)cmap1_addr)];
+
pa = pmap_early_vtophys(bs_state.freemempos);
physmem_exclude_region(start_pa, pa - start_pa, EXFLAG_NOALLOC);
@@ -8231,6 +8248,7 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot,
vm_paddr_t pa;
pt_entry_t pte, *ptep, *newpte;
pt_entry_t bits, mask;
+ char *tmpptep;
int lvl, rv;
PMAP_LOCK_ASSERT(kernel_pmap, MA_OWNED);
@@ -8360,6 +8378,24 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot,
break;
}
+ tmpptep = 0;
+ if (tmpva <= (vm_offset_t)ptep &&
+ tmpva + pte_size > (vm_offset_t)ptep) {
+ vm_paddr_t pte_pa;
+
+ mtx_lock(&cmap_lock);
+ tmpptep = cmap1_addr;
+ pte_pa = DMAP_TO_PHYS((vm_offset_t)ptep);
+ pmap_store(cmap1_pte, ATTR_AF |
+ pmap_sh_attr | ATTR_S1_AP(ATTR_S1_AP_RW) |
+ ATTR_S1_XN | ATTR_KERN_GP |
+ ATTR_S1_IDX(VM_MEMATTR_WRITE_BACK) |
+ PHYS_TO_PTE(pte_pa &~L3_OFFSET) | L3_PAGE);
+ dsb(ishst);
+ ptep = (pt_entry_t *)(tmpptep +
+ ((vm_offset_t)ptep & PAGE_MASK));
+ }
+
/* Update the entry */
pte = pmap_load(ptep);
pte &= ~mask;
@@ -8386,6 +8422,13 @@ pmap_change_props_locked(vm_offset_t va, vm_size_t size, vm_prot_t prot,
break;
}
+ if (tmpptep != 0) {
+ pmap_clear(cmap1_pte);
+ pmap_s1_invalidate_page(kernel_pmap,
+ (vm_offset_t)tmpptep, true);
+ mtx_unlock(&cmap_lock);
+ }
+
pa = PTE_TO_PHYS(pte);
if (!VIRT_IN_DMAP(tmpva) && PHYS_IN_DMAP(pa)) {
/*
@@ -9972,6 +10015,9 @@ sysctl_kmaps_dump(struct sbuf *sb, struct pmap_kernel_map_range *range,
case ATTR_S1_IDX(VM_MEMATTR_WRITE_THROUGH):
mode = "WT";
break;
+ case ATTR_S1_IDX(VM_MEMATTR_TAGGED):
+ mode = "TAGGED";
+ break;
default:
printf(
"%s: unknown memory type %x for range 0x%016lx-0x%016lx\n",
diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c
index ad461aa1bffc..1178817108e5 100644
--- a/sys/arm64/arm64/trap.c
+++ b/sys/arm64/arm64/trap.c
@@ -94,6 +94,7 @@ typedef void (abort_handler)(struct thread *, struct trapframe *, uint64_t,
static abort_handler align_abort;
static abort_handler data_abort;
static abort_handler external_abort;
+static abort_handler tag_check_abort;
static abort_handler *abort_handlers[] = {
[ISS_DATA_DFSC_TF_L0] = data_abort,
@@ -106,6 +107,7 @@ static abort_handler *abort_handlers[] = {
[ISS_DATA_DFSC_PF_L1] = data_abort,
[ISS_DATA_DFSC_PF_L2] = data_abort,
[ISS_DATA_DFSC_PF_L3] = data_abort,
+ [ISS_DATA_DFSC_TAG] = tag_check_abort,
[ISS_DATA_DFSC_ALIGN] = align_abort,
[ISS_DATA_DFSC_EXT] = external_abort,
[ISS_DATA_DFSC_EXT_L0] = external_abort,
@@ -250,6 +252,26 @@ external_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
panic("Unhandled external data abort");
}
+static void
+tag_check_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
+ uint64_t far, int lower)
+{
+ /*
+ * A Tag Check Fault should be handled as a SIGSEGV if it occurs
+ * at EL0 and a kernel panic if at EL1.
+ */
+ if (!lower) {
+ print_registers(frame);
+ print_gp_register("far", far);
+ printf(" esr: 0x%.16lx\n", esr);
+ panic("Tag Check Fault");
+ }
+
+ call_trapsignal(td, SIGSEGV, SEGV_MTESERR, (void *)far,
+ ESR_ELx_EXCEPTION(frame->tf_esr));
+ userret(td, frame);
+}
+
/*
* It is unsafe to access the stack canary value stored in "td" until
* kernel map translation faults are handled, see the pmap_klookup() call below.
diff --git a/sys/arm64/include/armreg.h b/sys/arm64/include/armreg.h
index 271fe693cdea..6447f0064d33 100644
--- a/sys/arm64/include/armreg.h
+++ b/sys/arm64/include/armreg.h
@@ -167,30 +167,76 @@
#define APIBKeyLo_EL1_op2 2
/* CCSIDR_EL1 - Cache Size ID Register */
-#define CCSIDR_NumSets_MASK 0x0FFFE000
-#define CCSIDR_NumSets64_MASK 0x00FFFFFF00000000
-#define CCSIDR_NumSets_SHIFT 13
-#define CCSIDR_NumSets64_SHIFT 32
-#define CCSIDR_Assoc_MASK 0x00001FF8
-#define CCSIDR_Assoc64_MASK 0x0000000000FFFFF8
+#define CCSIDR_EL1_REG MRS_REG_ALT_NAME(CCSIDR_EL1)
+#define CCSIDR_EL1_op0 2
+#define CCSIDR_EL1_op1 1
+#define CCSIDR_EL1_CRn 0
+#define CCSIDR_EL1_CRm 0
+#define CCSIDR_EL1_op2 0
+#define CCSIDR_LineSize_SHIFT 0
+#define CCSIDR_LineSize_WIDTH 3
+#define CCSIDR_LineSize_MASK (UL(0x7) << CCSIDR_LineSize_SHIFT)
+#define CCSIDR_LineSize_VAL(x) ((x) & CCSIDR_LineSize_MASK)
#define CCSIDR_Assoc_SHIFT 3
+#define CCSIDR_Assoc_WIDTH 10
+#define CCSIDR_Assoc_MASK (UL(0x3ff) << CCSIDR_Assoc_SHIFT)
+#define CCSIDR_Assoc_VAL(x) ((x) & CCSIDR_Assoc_MASK)
+#define CCSIDR_NumSets_SHIFT 13
+#define CCSIDR_NumSets_WIDTH 15
+#define CCSIDR_NumSets_MASK (UL(0x7fff) << CCSIDR_NumSets_SHIFT)
+#define CCSIDR_NumSets_VAL(x) ((x) & CCSIDR_NumSets_MASK)
+/* FEAT_CCIDX - Extended Cache Index */
#define CCSIDR_Assoc64_SHIFT 3
-#define CCSIDR_LineSize_MASK 0x7
-#define CCSIDR_NSETS(idr) \
+#define CCSIDR_Assoc64_WIDTH 20
+#define CCSIDR_Assoc64_MASK (UL(0x1fffff) << CCSIDR_Assoc64_SHIFT)
+#define CCSIDR_Assoc64_VAL(x) ((x) & CCSIDR_Assoc64_MASK)
+#define CCSIDR_NumSets64_SHIFT 32
+#define CCSIDR_NumSets64_WIDTH 23
+#define CCSIDR_NumSets64_MASK (UL(0xffffff) << CCSIDR_NumSets64_SHIFT)
+#define CCSIDR_NumSets64_VAL(x) ((x) & CCSIDR_NumSets64_MASK)
+#define CCSIDR_NumSets(idr) \
(((idr) & CCSIDR_NumSets_MASK) >> CCSIDR_NumSets_SHIFT)
-#define CCSIDR_ASSOC(idr) \
- (((idr) & CCSIDR_Assoc_MASK) >> CCSIDR_Assoc_SHIFT)
-#define CCSIDR_NSETS_64(idr) \
+#define CCSIDR_NumSets64(idr) \
(((idr) & CCSIDR_NumSets64_MASK) >> CCSIDR_NumSets64_SHIFT)
-#define CCSIDR_ASSOC_64(idr) \
+#define CCSIDR_Assoc(idr) \
+ (((idr) & CCSIDR_Assoc_MASK) >> CCSIDR_Assoc_SHIFT)
+#define CCSIDR_Assoc64(idr) \
(((idr) & CCSIDR_Assoc64_MASK) >> CCSIDR_Assoc64_SHIFT)
/* CLIDR_EL1 - Cache level ID register */
-#define CLIDR_CTYPE_MASK 0x7 /* Cache type mask bits */
-#define CLIDR_CTYPE_IO 0x1 /* Instruction only */
-#define CLIDR_CTYPE_DO 0x2 /* Data only */
-#define CLIDR_CTYPE_ID 0x3 /* Split instruction and data */
-#define CLIDR_CTYPE_UNIFIED 0x4 /* Unified */
+#define CLIDR_EL1_REG MRS_REG_ALT_NAME(CLIDR_EL1)
+#define CLIDR_EL1_op0 2
+#define CLIDR_EL1_op1 1
+#define CLIDR_EL1_CRn 0
+#define CLIDR_EL1_CRm 0
+#define CLIDR_EL1_op2 1
+#define CLIDR_CTYPE_MASK UL(0x7)
+#define CLIDR_CTYPE_NONE 0x0 /* No cache */
+#define CLIDR_CTYPE_IC 0x1 /* Instruction cache only */
+#define CLIDR_CTYPE_DC 0x2 /* Data cache only */
+#define CLIDR_CTYPE_IO 0x3 /* Separate instruction & data cache */
+#define CLIDR_CTYPE_UNIFIED 0x4 /* Unified cache */
+#define CLIDR_LoUIS_SHIFT 21
+#define CLIDR_LoUIS_WIDTH 3
+#define CLIDR_LoUIS_MASK (UL(0x7) << CLIDR_LoUIS_SHIFT)
+#define CLIDR_LoUIS_VAL(x) ((x) & CLIDR_LoUIS_MASK)
+#define CLIDR_LoC_SHIFT 24
+#define CLIDR_LoC_WIDTH 3
+#define CLIDR_LoC_MASK (UL(0x7) << CLIDR_LoC_SHIFT)
+#define CLIDR_LoC_VAL(x) ((x) & CLIDR_LoC_MASK)
+#define CLIDR_LoUU_SHIFT 27
+#define CLIDR_LoUU_WIDTH 3
+#define CLIDR_LoUU_MASK (UL(0x7) << CLIDR_LoUU_SHIFT)
+#define CLIDR_LoUU_VAL(x) ((x) & CLIDR_LoUU_MASK)
+#define CLIDR_ICB_SHIFT 30
+#define CLIDR_ICB_WIDTH 3
+#define CLIDR_ICB_MASK (UL(0x7) << CLIDR_ICB_SHIFT)
+#define CLIDR_ICB_VAL(x) ((x) & CLIDR_ICB_MASK)
+#define CLIDR_TTYPE_MASK UL(0x7)
+#define CLIDR_TTYPE_NONE 0x0 /* No tag cache */
+#define CLIDR_TTYPE_SAT 0x1 /* Separate Allocation Tag cache */
+#define CLIDR_TTYPE_UATU 0x2 /* Unified Allocation Tag, unified lines */
+#define CLIDR_TTYPE_UATS 0x3 /* Unified Allocation Tag, separate lines */
/* CNTKCTL_EL1 - Counter-timer Kernel Control Register */
#define CNTKCTL_EL1_op0 3
@@ -342,8 +388,28 @@
#define CPACR_EL12_op2 2
/* CSSELR_EL1 - Cache size selection register */
-#define CSSELR_Level(i) (i << 1)
-#define CSSELR_InD 0x00000001
+#define CSSELR_EL1_REG MRS_REG_ALT_NAME(CSSELR_EL1)
+#define CSSELR_EL1_op0 3
+#define CSSELR_EL1_op1 2
+#define CSSELR_EL1_CRn 0
+#define CSSELR_EL1_CRm 0
+#define CSSELR_EL1_op2 0
+#define CSSELR_InD_SHIFT 0
+#define CSSELR_InD_WIDTH 1
+#define CSSELR_InD_MASK (UL(0x1) << CSSELR_InD_SHIFT)
+#define CSSELR_InD_VAL(x) ((x) & CSSELR_InD_MASK)
+#define CSSELR_InD_DC (0x0 << CSSELR_InD_SHIFT) /* Data or unified cache */
+#define CSSELR_InD_IC (0x1 << CSSELR_InD_SHIFT) /* Instruction cache */
+#define CSSELR_Level_SHIFT 1
+#define CSSELR_Level_WIDTH 3
+#define CSSELR_Level_MASK (UL(0x7) << CSSELR_Level_SHIFT)
+#define CSSELR_Level(i) (i << CSSELR_Level_SHIFT)
+#define CSSELR_TnD_SHIFT 4
+#define CSSELR_TnD_WIDTH 1
+#define CSSELR_TnD_MASK (UL(0x1) << CSSELR_TnD_SHIFT)
+#define CSSELR_TnD_VAL(x) ((x) & CSSELR_TnD_MASK)
+#define CSSELR_TnD_DIU (0x0 << CSSELR_TnD_SHIFT) /* Data, Instruction or Unified cache */
+#define CSSELR_TnD_SAT (0x1 << CSSELR_TnD_SHIFT) /* Separate Allocation Tag cache */
/* CTR_EL0 - Cache Type Register */
#define CTR_EL0_REG MRS_REG_ALT_NAME(CTR_EL0)
@@ -645,6 +711,7 @@
#define ISS_DATA_DFSC_PF_L2 (0x0e << 0)
#define ISS_DATA_DFSC_PF_L3 (0x0f << 0)
#define ISS_DATA_DFSC_EXT (0x10 << 0)
+#define ISS_DATA_DFSC_TAG (0x11 << 0)
#define ISS_DATA_DFSC_EXT_L0 (0x14 << 0)
#define ISS_DATA_DFSC_EXT_L1 (0x15 << 0)
#define ISS_DATA_DFSC_EXT_L2 (0x16 << 0)
@@ -721,6 +788,30 @@
#define FAR_EL12_CRm 0
#define FAR_EL12_op2 0
+/* GCR_EL1 - Tag Control Register */
+#define GCR_EL1_REG MRS_REG_ALT_NAME(GCR_EL1)
+#define GCR_EL1_op0 3
+#define GCR_EL1_op1 0
+#define GCR_EL1_CRn 1
+#define GCR_EL1_CRm 0
+#define GCR_EL1_op2 6
+#define GCR_Exclude_SHIFT 0
+#define GCR_Exclude_MASK (UL(0xffff) << GCR_Exclude_SHIFT)
+#define GCR_RRND_SHIFT 16
+#define GCR_RRND (UL(0x1) << GCR_RRND_SHIFT)
+
+/* GMID_EL1 - Multiple tag transfer ID Register */
+#define GMID_EL1_REG MRS_REG_ALT_NAME(GMID_EL1)
+#define GMID_EL1_op0 3
+#define GMID_EL1_op1 1
+#define GMID_EL1_CRn 0
+#define GMID_EL1_CRm 0
+#define GMID_EL1_op2 4
+#define GMID_BS_SHIFT 0
+#define GMID_BS_WIDTH 4
+#define GMID_BS_MASK (UL(0xf) << GMID_BS_SHIFT)
+#define GMID_BS_SIZE(reg) (((reg) & GMID_BS_MASK) >> GMID_BS_SHIFT)
+
/* ICC_CTLR_EL1 */
#define ICC_CTLR_EL1_EOIMODE (1U << 1)
@@ -1988,6 +2079,7 @@
#define MAIR_DEVICE_nGnRE UL(0x04)
#define MAIR_NORMAL_NC UL(0x44)
#define MAIR_NORMAL_WT UL(0xbb)
+#define MAIR_NORMAL_TG UL(0xf0)
#define MAIR_NORMAL_WB UL(0xff)
/* MAIR_EL12 */
@@ -2601,6 +2693,18 @@
#define PMXEVTYPER_EL0_CRm 13
#define PMXEVTYPER_EL0_op2 1
+/* RGSR_EL1 - Random Allocation Tag Seed Register */
+#define RGSR_EL1_REG MRS_REG_ALT_NAME(RGSR_EL1)
+#define RGSR_EL1_op0 3
+#define RGSR_EL1_op1 0
+#define RGSR_EL1_CRn 1
+#define RGSR_EL1_CRm 0
+#define RGSR_EL1_op2 5
+#define RGSR_TAG_SHIFT 0
+#define RGSR_TAG_MASK (UL(0xf) << RGSR_TAG_SHIFT)
+#define RGSR_SEED_SHIFT 8
+#define RGSR_SEED_MASK (UL(0xffff) << RGSR_SEED_SHIFT)
+
/* RNDRRS */
#define RNDRRS_REG MRS_REG_ALT_NAME(RNDRRS)
#define RNDRRS_op0 3
@@ -2655,8 +2759,18 @@
#define SCTLR_BT0 (UL(0x1) << 35)
#define SCTLR_BT1 (UL(0x1) << 36)
#define SCTLR_ITFSB (UL(0x1) << 37)
-#define SCTLR_TCF0_MASK (UL(0x3) << 38)
-#define SCTLR_TCF_MASK (UL(0x3) << 40)
+#define SCTLR_TCF0_SHIFT 38
+#define SCTLR_TCF0_MASK (UL(0x3) << SCTLR_TCF0_SHIFT)
+#define SCTLR_TCF0_NONE (UL(0x0) << SCTLR_TCF0_SHIFT)
+#define SCTLR_TCF0_SYNC (UL(0x1) << SCTLR_TCF0_SHIFT)
+#define SCTLR_TCF0_ASYNC (UL(0x2) << SCTLR_TCF0_SHIFT)
+#define SCTLR_TCF0_ASYM (UL(0x3) << SCTLR_TCF0_SHIFT)
+#define SCTLR_TCF_SHIFT 40
+#define SCTLR_TCF_MASK (UL(0x3) << SCTLR_TCF_SHIFT)
+#define SCTLR_TCF_NONE (UL(0x0) << SCTLR_TCF_SHIFT)
+#define SCTLR_TCF_SYNC (UL(0x1) << SCTLR_TCF_SHIFT)
+#define SCTLR_TCF_ASYNC (UL(0x2) << SCTLR_TCF_SHIFT)
+#define SCTLR_TCF_ASYM (UL(0x3) << SCTLR_TCF_SHIFT)
#define SCTLR_ATA0 (UL(0x1) << 42)
#define SCTLR_ATA (UL(0x1) << 43)
#define SCTLR_DSSBS (UL(0x1) << 44)
@@ -2766,6 +2880,15 @@
#define REVIDR_EL1_CRm 0
#define REVIDR_EL1_op2 6
+/* TCO - Tag Check Override */
+#define TCO MRS(TCO)
+#define TCO_REG MRS_REG_ALT_NAME(TCO)
+#define TCO_op0 3
+#define TCO_op1 3
+#define TCO_CRn 4
+#define TCO_CRm 2
+#define TCO_op2 7
+
/* TCR_EL1 - Translation Control Register */
#define TCR_EL1_REG MRS_REG_ALT_NAME(TCR_EL1)
#define TCR_EL1_op0 3
@@ -2881,6 +3004,30 @@
#define TCR_EL12_CRm 0
#define TCR_EL12_op2 2
+/* TFSRE0_EL1 - Tag Fault Status Register (EL0) */
+#define TFSRE0_EL1_REG MRS_REG_ALT_NAME(TFSRE0_EL1)
+#define TFSRE0_EL1_op0 3
+#define TFSRE0_EL1_op1 0
+#define TFSRE0_EL1_CRn 5
+#define TFSRE0_EL1_CRm 6
+#define TFSRE0_EL1_op2 1
+#define TFSRE0_TF0_SHIFT 0
+#define TFSRE0_TF0_MASK (UL(0x1) << TFSRE0_TF0_SHIFT)
+#define TFSRE0_TF1_SHIFT 1
+#define TFSRE0_TF1_MASK (UL(0x1) << TFSRE0_TF1_SHIFT)
+
+/* TFSR_EL1 - Tag Fault Status Register */
+#define TFSR_EL1_REG MRS_REG_ALT_NAME(TFSR_EL1)
+#define TFSR_EL1_op0 3
+#define TFSR_EL1_op1 0
+#define TFSR_EL1_CRn 5
+#define TFSR_EL1_CRm 6
+#define TFSR_EL1_op2 0
+#define TFSR_TF0_SHIFT 0
+#define TFSR_TF0_MASK (UL(0x1) << TFSR_TF0_SHIFT)
+#define TFSR_TF1_SHIFT 1
+#define TFSR_TF1_MASK (UL(0x1) << TFSR_TF1_SHIFT)
+
/* TTBR0_EL1 & TTBR1_EL1 - Translation Table Base Register 0 & 1 */
#define TTBR_ASID_SHIFT 48
#define TTBR_ASID_MASK (0xfffful << TTBR_ASID_SHIFT)
diff --git a/sys/arm64/include/hypervisor.h b/sys/arm64/include/hypervisor.h
index 3ee5c12f2265..73adf89b4182 100644
--- a/sys/arm64/include/hypervisor.h
+++ b/sys/arm64/include/hypervisor.h
@@ -2062,6 +2062,16 @@
#define SCTLR_EL2_EIS (0x1UL << SCTLR_EL2_EIS_SHIFT)
#define SCTLR_EL2_EE_SHIFT 25
#define SCTLR_EL2_EE (0x1UL << SCTLR_EL2_EE_SHIFT)
+#define SCTLR_EL2_ITFSB_SHIFT 37
+#define SCTLR_EL2_ITFSB (0x1UL << SCTLR_EL2_ITFSB_SHIFT)
+#define SCTLR_EL2_TCF0_SHIFT 38
+#define SCTLR_EL2_TCF0 (0x2UL << SCTLR_EL2_TCF0_SHIFT)
+#define SCTLR_EL2_TCF_SHIFT 40
+#define SCTLR_EL2_TCF (0x2UL << SCTLR_EL2_TCF_SHIFT)
+#define SCTLR_EL2_ATA0_SHIFT 42
+#define SCTLR_EL2_ATA0 (0x1UL << SCTLR_EL2_ATA0_SHIFT)
+#define SCTLR_EL2_ATA_SHIFT 43
+#define SCTLR_EL2_ATA (0x1UL << SCTLR_EL2_ATA_SHIFT)
/* TCR_EL2 - Translation Control Register */
#define TCR_EL2_RES1 ((0x1UL << 31) | (0x1UL << 23))
@@ -2104,6 +2114,20 @@
#define TCR_EL2_HWU62 (1UL << TCR_EL2_HWU62_SHIFT)
#define TCR_EL2_HWU \
(TCR_EL2_HWU59 | TCR_EL2_HWU60 | TCR_EL2_HWU61 | TCR_EL2_HWU62)
+#define TCR_EL2_TCMA_SHIFT 30
+#define TCR_EL2_TCMA (1UL << TCR_EL2_TCMA_SHIFT)
+
+/* TFSR_EL2 - Tag Fault Status Register EL2 */
+#define TFSR_EL2_REG MRS_REG_ALT_NAME(TFSR_EL2)
+#define TFSR_EL2_op0 3
+#define TFSR_EL2_op1 4
+#define TFSR_EL2_CRn 5
+#define TFSR_EL2_CRm 6
+#define TFSR_EL2_op2 0
+#define TFSR_TF0_SHIFT 0
+#define TFSR_TF0_MASK (UL(0x1) << TFSR_TF0_SHIFT)
+#define TFSR_TF1_SHIFT 1
+#define TFSR_TF1_MASK (UL(0x1) << TFSR_TF1_SHIFT)
/* VMPDIR_EL2 - Virtualization Multiprocessor ID Register */
#define VMPIDR_EL2_U 0x0000000040000000
diff --git a/sys/arm64/include/vm.h b/sys/arm64/include/vm.h
index e03e615bb841..4d9f2860f654 100644
--- a/sys/arm64/include/vm.h
+++ b/sys/arm64/include/vm.h
@@ -33,7 +33,8 @@
#define VM_MEMATTR_WRITE_BACK 2
#define VM_MEMATTR_WRITE_THROUGH 3
#define VM_MEMATTR_DEVICE_nGnRE 4
-#define VM_MEMATTR_END (VM_MEMATTR_DEVICE_nGnRE + 1)
+#define VM_MEMATTR_TAGGED 5
+#define VM_MEMATTR_END (VM_MEMATTR_TAGGED + 1)
#define VM_MEMATTR_DEVICE VM_MEMATTR_DEVICE_nGnRE
#define VM_MEMATTR_DEVICE_NP VM_MEMATTR_DEVICE_nGnRnE
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
index 24abeb66ea51..a9c2a0906ef2 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
@@ -35,21 +35,21 @@ vcmn_err(int ce, const char *fmt, va_list adx)
prefix = NULL; /* silence unwitty compilers */
switch (ce) {
case CE_CONT:
- prefix = "Solaris(cont): ";
+ prefix = "dtrace(cont): ";
break;
case CE_NOTE:
- prefix = "Solaris: NOTICE: ";
+ prefix = "dtrace: NOTICE: ";
break;
case CE_WARN:
- prefix = "Solaris: WARNING: ";
+ prefix = "dtrace: WARNING: ";
break;
case CE_PANIC:
- prefix = "Solaris(panic): ";
+ prefix = "dtrace(panic): ";
break;
case CE_IGNORE:
break;
default:
- panic("Solaris: unknown severity level");
+ panic("dtrace: unknown severity level");
}
if (ce == CE_PANIC) {
vsnprintf(buf, sizeof(buf), fmt, adx);
@@ -76,7 +76,7 @@ int
assfail(const char *a, const char *f, int l)
{
- panic("solaris assert: %s, file: %s, line: %d", a, f, l);
+ panic("dtrace assert: %s, file: %s, line: %d", a, f, l);
return (0);
}
@@ -86,6 +86,6 @@ assfail3(const char *a, uintmax_t lv, const char *op, uintmax_t rv,
const char *f, int l)
{
- panic("solaris assert: %s (0x%jx %s 0x%jx), file: %s, line: %d",
+ panic("dtrace assert: %s (0x%jx %s 0x%jx), file: %s, line: %d",
a, lv, op, rv, f, l);
}
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 4ec6dd452b32..a0b6118900ed 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -2649,7 +2649,7 @@ freebsd11_freebsd32_nstat(struct thread *td,
if (error != 0)
return (error);
error = freebsd11_cvtnstat32(&sb, &nsb);
- if (error != 0)
+ if (error == 0)
error = copyout(&nsb, uap->ub, sizeof (nsb));
return (error);
}
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index 941b76788dc1..786a486b9143 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -659,8 +659,7 @@ linprocfs_dopartitions(PFS_FILL_ARGS)
int major, minor;
g_topology_lock();
- sbuf_printf(sb, "major minor #blocks name rio rmerge rsect "
- "ruse wio wmerge wsect wuse running use aveq\n");
+ sbuf_printf(sb, "major minor #blocks name\n\n");
LIST_FOREACH(cp, &g_classes, class) {
if (strcmp(cp->name, "DISK") == 0 ||
@@ -672,13 +671,10 @@ linprocfs_dopartitions(PFS_FILL_ARGS)
major = 0;
minor = 0;
}
- sbuf_printf(sb, "%d %d %lld %s "
- "%d %d %d %d %d "
- "%d %d %d %d %d %d\n",
- major, minor,
- (long long)pp->mediasize, pp->name,
- 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0);
+ sbuf_printf(sb, "%4d %7d %10lld %s\n",
+ major, minor,
+ B2K((long long)pp->mediasize),
+ pp->name);
}
}
}
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index d2fa0331026b..b68e950a2dcf 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -43,6 +43,7 @@
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/proc.h>
+#include <sys/vnode.h>
#include <sys/sbuf.h>
#include <sys/sockio.h>
#include <sys/soundcard.h>
@@ -59,6 +60,7 @@
#include <dev/evdev/input.h>
#include <dev/hid/hidraw.h>
+#include <dev/iicbus/iic.h>
#include <dev/usb/usb_ioctl.h>
#ifdef COMPAT_LINUX32
@@ -100,6 +102,7 @@ DEFINE_LINUX_IOCTL_SET(vfat, VFAT);
DEFINE_LINUX_IOCTL_SET(console, CONSOLE);
DEFINE_LINUX_IOCTL_SET(hdio, HDIO);
DEFINE_LINUX_IOCTL_SET(disk, DISK);
+DEFINE_LINUX_IOCTL_SET(i2c, I2C);
DEFINE_LINUX_IOCTL_SET(socket, SOCKET);
DEFINE_LINUX_IOCTL_SET(sound, SOUND);
DEFINE_LINUX_IOCTL_SET(termio, TERMIO);
@@ -160,6 +163,18 @@ struct linux_hd_big_geometry {
uint32_t start;
};
+struct linux_i2c_msg {
+ uint16_t addr;
+ uint16_t flags;
+ uint16_t len;
+ l_uintptr_t buf;
+};
+
+struct linux_i2c_rdwr_data {
+ l_uintptr_t msgs;
+ l_uint nmsgs;
+};
+
static int
linux_ioctl_hdio(struct thread *td, struct linux_ioctl_args *args)
{
@@ -3640,6 +3655,106 @@ linux_ioctl_nvme(struct thread *td, struct linux_ioctl_args *args)
#endif
static int
+linux_ioctl_i2c(struct thread *td, struct linux_ioctl_args *args)
+{
+ struct linux_i2c_rdwr_data lrdwr;
+ struct linux_i2c_msg *lmsgs = NULL;
+ struct iic_rdwr_data rdwr;
+ struct iic_msg *msgs = NULL;
+ struct file *fp;
+ iic_linux_rdwr_t *linux_rdwr;
+ l_ulong funcs;
+ uint16_t lflags;
+ uint8_t addr;
+ int error;
+ l_uint i;
+
+ error = fget(td, args->fd, &cap_ioctl_rights, &fp);
+ if (error != 0)
+ return (error);
+
+ linux_rdwr = NULL;
+ if (fp->f_type == DTYPE_VNODE && fp->f_vnode != NULL &&
+ fp->f_vnode->v_rdev != NULL)
+ linux_rdwr = (iic_linux_rdwr_t *)fp->f_vnode->v_rdev->si_drv2;
+
+ switch (args->cmd & 0xffff) {
+ case LINUX_I2C_RETRIES:
+ case LINUX_I2C_TIMEOUT:
+ case LINUX_I2C_PEC:
+ error = 0;
+ break;
+ case LINUX_I2C_TENBIT:
+ error = (args->arg == 0) ? 0 : ENOTSUP;
+ break;
+ case LINUX_I2C_FUNCS:
+ funcs = LINUX_I2C_FUNC_I2C | LINUX_I2C_FUNC_NOSTART;
+ error = copyout(&funcs, (void *)args->arg, sizeof(funcs));
+ break;
+ case LINUX_I2C_SLAVE:
+ case LINUX_I2C_SLAVE_FORCE:
+ if (args->arg > 0x7f) {
+ error = EINVAL;
+ break;
+ }
+ addr = (uint8_t)(args->arg << 1);
+ error = fo_ioctl(fp, I2CSADDR, (caddr_t)&addr, td->td_ucred, td);
+ break;
+ case LINUX_I2C_RDWR:
+ error = copyin((void *)args->arg, &lrdwr, sizeof(lrdwr));
+ if (error != 0)
+ break;
+ if (lrdwr.nmsgs > IIC_RDRW_MAX_MSGS) {
+ error = EINVAL;
+ break;
+ }
+ lmsgs = malloc(sizeof(*lmsgs) * lrdwr.nmsgs, M_TEMP, M_WAITOK);
+ msgs = malloc(sizeof(*msgs) * lrdwr.nmsgs, M_TEMP, M_WAITOK);
+ error = copyin((void *)(uintptr_t)lrdwr.msgs, lmsgs,
+ sizeof(*lmsgs) * lrdwr.nmsgs);
+ if (error != 0)
+ break;
+ for (i = 0; i < lrdwr.nmsgs; i++) {
+ lflags = lmsgs[i].flags;
+ if (lmsgs[i].addr > 0x7f || (lflags & LINUX_I2C_M_TEN) != 0) {
+ error = ENOTSUP;
+ break;
+ }
+ if ((lflags & ~(LINUX_I2C_M_RD | LINUX_I2C_M_NOSTART)) != 0) {
+ error = ENOTSUP;
+ break;
+ }
+ msgs[i].slave = lmsgs[i].addr << 1;
+ msgs[i].flags = (lflags & LINUX_I2C_M_RD) ? IIC_M_RD : IIC_M_WR;
+ if ((lflags & LINUX_I2C_M_NOSTART) != 0)
+ msgs[i].flags |= IIC_M_NOSTART;
+ msgs[i].len = lmsgs[i].len;
+ msgs[i].buf = (uint8_t *)(uintptr_t)lmsgs[i].buf;
+ }
+ if (error == 0) {
+ if (linux_rdwr == NULL) {
+ error = ENOTTY;
+ } else {
+ rdwr.msgs = msgs;
+ rdwr.nmsgs = lrdwr.nmsgs;
+ error = linux_rdwr(fp, &rdwr, fp->f_flag, td);
+ if (error == 0)
+ td->td_retval[0] = lrdwr.nmsgs;
+ }
+ }
+ break;
+ case LINUX_I2C_SMBUS:
+ default:
+ error = ENOTSUP;
+ break;
+ }
+ free(msgs, M_TEMP);
+ free(lmsgs, M_TEMP);
+ fdrop(fp, td);
+ return (error);
+}
+
+static int
linux_ioctl_hidraw(struct thread *td, struct linux_ioctl_args *args)
{
int len = (args->cmd & 0x3fff0000) >> 16;
diff --git a/sys/compat/linux/linux_ioctl.h b/sys/compat/linux/linux_ioctl.h
index 116a4e676228..769f56b7de51 100644
--- a/sys/compat/linux/linux_ioctl.h
+++ b/sys/compat/linux/linux_ioctl.h
@@ -72,6 +72,30 @@
#define LINUX_IOCTL_HDIO_MAX LINUX_HDIO_GET_GEO_BIG
/*
+ * i2c
+ */
+#define LINUX_I2C_RETRIES 0x0701
+#define LINUX_I2C_TIMEOUT 0x0702
+#define LINUX_I2C_SLAVE 0x0703
+#define LINUX_I2C_TENBIT 0x0704
+#define LINUX_I2C_FUNCS 0x0705
+#define LINUX_I2C_SLAVE_FORCE 0x0706
+#define LINUX_I2C_RDWR 0x0707
+#define LINUX_I2C_PEC 0x0708
+#define LINUX_I2C_SMBUS 0x0720
+
+#define LINUX_IOCTL_I2C_MIN LINUX_I2C_RETRIES
+#define LINUX_IOCTL_I2C_MAX LINUX_I2C_SMBUS
+
+#define LINUX_I2C_M_RD 0x0001
+#define LINUX_I2C_M_TEN 0x0010
+#define LINUX_I2C_M_NOSTART 0x4000
+
+#define LINUX_I2C_FUNC_I2C 0x00000001UL
+#define LINUX_I2C_FUNC_10BIT_ADDR 0x00000002UL
+#define LINUX_I2C_FUNC_NOSTART 0x00000010UL
+
+/*
* cdrom
*/
#define LINUX_CDROMPAUSE 0x5301
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index 023be1e6b885..29b55ef60357 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -30,6 +30,7 @@
#include <sys/param.h>
#include <sys/capsicum.h>
+#include <sys/domain.h>
#include <sys/filedesc.h>
#include <sys/limits.h>
#include <sys/malloc.h>
@@ -52,6 +53,7 @@
#include <netinet/ip.h>
#include <netinet/tcp.h>
#ifdef INET6
+#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
@@ -622,6 +624,19 @@ bsd_to_linux_tcp_user_timeout(u_int bsd_timeout)
return (bsd_timeout * 1000U);
}
+#ifdef INET6
+static int
+linux_to_bsd_icmp6_sockopt(int opt)
+{
+
+ switch (opt) {
+ case LINUX_ICMP6_FILTER:
+ return (ICMP6_FILTER);
+ }
+ return (-1);
+}
+#endif
+
static int
linux_to_bsd_msg_flags(int flags)
{
@@ -692,6 +707,20 @@ bsd_to_linux_ip_cmsg_type(int cmsg_type)
return (-1);
}
+#ifdef INET6
+static int
+bsd_to_linux_ip6_cmsg_type(int cmsg_type)
+{
+ switch (cmsg_type) {
+ case IPV6_2292HOPLIMIT:
+ return (LINUX_IPV6_2292HOPLIMIT);
+ case IPV6_HOPLIMIT:
+ return (LINUX_IPV6_HOPLIMIT);
+ }
+ return (-1);
+}
+#endif
+
static int
bsd_to_linux_cmsg_type(struct proc *p, int cmsg_type, int cmsg_level)
{
@@ -699,6 +728,10 @@ bsd_to_linux_cmsg_type(struct proc *p, int cmsg_type, int cmsg_level)
if (cmsg_level == IPPROTO_IP)
return (bsd_to_linux_ip_cmsg_type(cmsg_type));
+#ifdef INET6
+ if (cmsg_level == IPPROTO_IPV6)
+ return (bsd_to_linux_ip6_cmsg_type(cmsg_type));
+#endif
if (cmsg_level != SOL_SOCKET)
return (-1);
@@ -2175,6 +2208,49 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
break;
}
break;
+#ifdef INET6
+ case IPPROTO_RAW: {
+ struct file *fp;
+ struct socket *so;
+ int family;
+
+ error = getsock(td, args->s, &cap_setsockopt_rights, &fp);
+ if (error != 0)
+ return (error);
+ so = fp->f_data;
+ family = so->so_proto->pr_domain->dom_family;
+ fdrop(fp, td);
+
+ name = -1;
+ if (family == AF_INET6) {
+ name = linux_to_bsd_ip6_sockopt(args->optname);
+ if (name >= 0)
+ level = IPPROTO_IPV6;
+ }
+ break;
+ }
+ case IPPROTO_ICMPV6: {
+ struct icmp6_filter f;
+ int i;
+
+ name = linux_to_bsd_icmp6_sockopt(args->optname);
+ if (name != ICMP6_FILTER)
+ break;
+
+ if (args->optlen != sizeof(f))
+ return (EINVAL);
+
+ error = copyin(PTRIN(args->optval), &f, sizeof(f));
+ if (error)
+ return (error);
+
+ /* Linux uses opposite values for pass/block in ICMPv6 */
+ for (i = 0; i < nitems(f.icmp6_filt); i++)
+ f.icmp6_filt[i] = ~f.icmp6_filt[i];
+ return (kern_setsockopt(td, args->s, IPPROTO_ICMPV6,
+ ICMP6_FILTER, &f, UIO_SYSSPACE, sizeof(f)));
+ }
+#endif
case SOL_NETLINK:
name = args->optname;
break;
@@ -2435,6 +2511,56 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
break;
}
break;
+#ifdef INET6
+ case IPPROTO_RAW: {
+ struct file *fp;
+ struct socket *so;
+ int family;
+
+ error = getsock(td, args->s, &cap_getsockopt_rights, &fp);
+ if (error != 0)
+ return (error);
+ so = fp->f_data;
+ family = so->so_proto->pr_domain->dom_family;
+ fdrop(fp, td);
+
+ name = -1;
+ if (family == AF_INET6) {
+ name = linux_to_bsd_ip6_sockopt(args->optname);
+ if (name >= 0)
+ level = IPPROTO_IPV6;
+ }
+ break;
+ }
+ case IPPROTO_ICMPV6: {
+ struct icmp6_filter f;
+ int i;
+
+ name = linux_to_bsd_icmp6_sockopt(args->optname);
+ if (name != ICMP6_FILTER)
+ break;
+
+ error = copyin(PTRIN(args->optlen), &len, sizeof(len));
+ if (error)
+ return (error);
+ if (len != sizeof(f))
+ return (EINVAL);
+
+ error = kern_getsockopt(td, args->s, IPPROTO_ICMPV6,
+ ICMP6_FILTER, &f, UIO_SYSSPACE, &len);
+ if (error)
+ return (error);
+
+ /* Linux uses opposite values for pass/block in ICMPv6 */
+ for (i = 0; i < nitems(f.icmp6_filt); i++)
+ f.icmp6_filt[i] = ~f.icmp6_filt[i];
+ error = copyout(&f, PTRIN(args->optval), len);
+ if (error)
+ return (error);
+
+ return (copyout(&len, PTRIN(args->optlen), sizeof(socklen_t)));
+ }
+#endif
default:
name = -1;
break;
diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h
index f2a96b3e7dcb..d30d68409496 100644
--- a/sys/compat/linux/linux_socket.h
+++ b/sys/compat/linux/linux_socket.h
@@ -324,6 +324,8 @@ int linux_accept(struct thread *td, struct linux_accept_args *args);
#define LINUX_TCP_MD5SIG 14
#define LINUX_TCP_USER_TIMEOUT 18
+#define LINUX_ICMP6_FILTER 1
+
struct l_ifmap {
l_ulong mem_start;
l_ulong mem_end;
diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c
index 03783d466211..6f96a219003b 100644
--- a/sys/compat/linux/linux_stats.c
+++ b/sys/compat/linux/linux_stats.c
@@ -295,26 +295,28 @@ struct l_statfs {
#define LINUX_ZFS_SUPER_MAGIC 0x2FC12FC1
#define LINUX_DEVFS_SUPER_MAGIC 0x1373L
#define LINUX_SHMFS_MAGIC 0x01021994
+#define LINUX_SYSFS_MAGIC 0x62656572
static long
bsd_to_linux_ftype(const char *fstypename)
{
int i;
static struct {const char *bsd_name; long linux_type;} b2l_tbl[] = {
- {"ufs", LINUX_UFS_SUPER_MAGIC},
- {"zfs", LINUX_ZFS_SUPER_MAGIC},
- {"cd9660", LINUX_ISOFS_SUPER_MAGIC},
- {"nfs", LINUX_NFS_SUPER_MAGIC},
- {"ext2fs", LINUX_EXT2_SUPER_MAGIC},
- {"procfs", LINUX_PROC_SUPER_MAGIC},
- {"msdosfs", LINUX_MSDOS_SUPER_MAGIC},
- {"ntfs", LINUX_NTFS_SUPER_MAGIC},
- {"nwfs", LINUX_NCP_SUPER_MAGIC},
- {"hpfs", LINUX_HPFS_SUPER_MAGIC},
- {"coda", LINUX_CODA_SUPER_MAGIC},
- {"devfs", LINUX_DEVFS_SUPER_MAGIC},
- {"tmpfs", LINUX_SHMFS_MAGIC},
- {NULL, 0L}};
+ {"ufs", LINUX_UFS_SUPER_MAGIC},
+ {"zfs", LINUX_ZFS_SUPER_MAGIC},
+ {"cd9660", LINUX_ISOFS_SUPER_MAGIC},
+ {"nfs", LINUX_NFS_SUPER_MAGIC},
+ {"ext2fs", LINUX_EXT2_SUPER_MAGIC},
+ {"procfs", LINUX_PROC_SUPER_MAGIC},
+ {"msdosfs", LINUX_MSDOS_SUPER_MAGIC},
+ {"ntfs", LINUX_NTFS_SUPER_MAGIC},
+ {"nwfs", LINUX_NCP_SUPER_MAGIC},
+ {"hpfs", LINUX_HPFS_SUPER_MAGIC},
+ {"coda", LINUX_CODA_SUPER_MAGIC},
+ {"devfs", LINUX_DEVFS_SUPER_MAGIC},
+ {"tmpfs", LINUX_SHMFS_MAGIC},
+ {"linsysfs", LINUX_SYSFS_MAGIC},
+ {NULL, 0L}};
for (i = 0; b2l_tbl[i].bsd_name != NULL; i++)
if (strcmp(b2l_tbl[i].bsd_name, fstypename) == 0)
diff --git a/sys/compat/linuxkpi/common/include/linux/kfifo.h b/sys/compat/linuxkpi/common/include/linux/kfifo.h
index fbe16e22683e..b0d0c17f07e4 100644
--- a/sys/compat/linuxkpi/common/include/linux/kfifo.h
+++ b/sys/compat/linuxkpi/common/include/linux/kfifo.h
@@ -89,7 +89,7 @@
(_kf)->head[(_kf)->last] = (_e); \
(_kf)->count++; \
(_kf)->last++; \
- if ((_kf)->last > (_kf)->total) \
+ if ((_kf)->last >= (_kf)->total) \
(_kf)->last = 0; \
_rc = true; \
} \
@@ -107,7 +107,7 @@
*(_e) = (_kf)->head[(_kf)->first]; \
(_kf)->count--; \
(_kf)->first++; \
- if ((_kf)->first > (_kf)->total) \
+ if ((_kf)->first >= (_kf)->total) \
(_kf)->first = 0; \
_rc = true; \
} \
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 4dda93e2ee70..d982164ae3a1 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -880,6 +880,10 @@ device vlan
# frames in UDP packets according to RFC7348.
device vxlan
+# The `geneve' device implements the GENEVE encapsulation of virtual
+# overlays according to RFC8926.
+device geneve
+
# The `wlan' device provides generic code to support 802.11
# drivers, including host AP mode; it is MANDATORY for the wi,
# and ath drivers and will eventually be required by all 802.11 drivers.
@@ -1993,6 +1997,9 @@ device vr # VIA Rhine, Rhine II
device vte # DM&P Vortex86 RDC R6040 Fast Ethernet
device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'')
+# MDIO bus for Ethernet NICs that directly expose the MDIO bus
+device mdio
+
# PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure
device iflib
device em # Intel Pro/1000 Gigabit Ethernet
diff --git a/sys/conf/files b/sys/conf/files
index 66999f9c5325..18ca17c731a8 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1775,6 +1775,7 @@ dev/gpio/gpio_if.m optional gpio
dev/gpio/gpiobus_if.m optional gpio
dev/gpio/gpiopps.c optional gpiopps fdt
dev/gpio/ofw_gpiobus.c optional fdt gpio
+dev/hid/appleir.c optional appleir
dev/hid/bcm5974.c optional bcm5974
dev/hid/hconf.c optional hconf
dev/hid/hcons.c optional hcons
@@ -2299,6 +2300,10 @@ dev/ixgbe/if_ix.c optional ix inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe -DSMP"
dev/ixgbe/if_ixv.c optional ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe -DSMP"
+dev/ixgbe/if_ix_mdio.c optional ix inet mdio \
+ compile-with "${NORMAL_C} -I$S/dev/ixgbe"
+dev/ixgbe/if_ix_mdio_hw.c optional ix inet mdio \
+ compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/if_bypass.c optional ix inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/if_fdir.c optional ix inet | ixv inet \
@@ -2315,6 +2320,8 @@ dev/ixgbe/ixgbe_api.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_common.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
+dev/ixgbe/ixgbe_fw_logging.c optional ix inet | ixv inet \
+ compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_mbx.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_vf.c optional ix inet | ixv inet \
@@ -4236,6 +4243,7 @@ net/if_stf.c optional stf inet inet6
net/if_tuntap.c optional tuntap
net/if_vlan.c optional vlan
net/if_vxlan.c optional vxlan inet | vxlan inet6
+net/if_geneve.c optional geneve inet | geneve inet6
net/ifdi_if.m optional ether pci iflib
net/iflib.c optional ether pci iflib
net/mp_ring.c optional ether iflib
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 59a65a8251ca..5d1b6e164a51 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -768,7 +768,20 @@ dev/ahci/ahci_fsl_fdt.c optional soc_nxp_ls ahci fdt
dev/flash/flexspi/flex_spi.c optional clk flex_spi soc_nxp_ls fdt
# Qualcomm
-arm64/qualcomm/qcom_gcc.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_main.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_clock.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_reset.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_ipq4018_reset.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_ipq4018_clock.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_msm8916_reset.c optional qcom_gcc fdt
+dev/qcom_gcc/qcom_gcc_msm8916_clock.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_fepll.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_fdiv.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_apssdiv.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_freqtbl.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_rcg2.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_branch2.c optional qcom_gcc fdt
+dev/qcom_clk/qcom_clk_ro_div.c optional qcom_gcc fdt
dev/qcom_mdio/qcom_mdio_ipq4018.c optional qcom_mdio fdt mdio mii
# RockChip Drivers
diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk
index 871cd1c0705a..3dc1efe3d5d2 100644
--- a/sys/conf/kern.pre.mk
+++ b/sys/conf/kern.pre.mk
@@ -158,9 +158,11 @@ NORMAL_FWO= ${CC:N${CCACHE_BIN}} -c ${ASM_CFLAGS} ${WERROR} -o ${.TARGET} \
NOSAN_C= ${NORMAL_C:N-fsanitize*:N-fno-sanitize*:N-fasan-shadow-offset*}
# for ZSTD in the kernel (include zstd/lib/freebsd before other CFLAGS)
-ZSTD_C= ${CC} -c -DZSTD_HEAPMODE=1 -I$S/contrib/zstd/lib/freebsd ${CFLAGS} \
+ZSTD_C= ${CC} -c -I$S/contrib/zstd/lib/freebsd ${CFLAGS} \
-I$S/contrib/zstd/lib -I$S/contrib/zstd/lib/common ${WERROR} \
- -Wno-missing-prototypes -U__BMI__ -DZSTD_NO_INTRINSICS ${.IMPSRC}
+ -Wno-missing-prototypes -U__BMI__ \
+ -DZSTD_HEAPMODE=1 -DZSTD_NO_INTRINSICS -DZSTD_NO_TRACE \
+ ${.IMPSRC}
# https://github.com/facebook/zstd/commit/812e8f2a [zstd 1.4.1]
# "Note that [GCC] autovectorization still does not do a good job on the
# optimized version, so it's turned off via attribute and flag. I found
diff --git a/sys/conf/ldscript.arm64 b/sys/conf/ldscript.arm64
index ae231c3037e6..7ec0d4324270 100644
--- a/sys/conf/ldscript.arm64
+++ b/sys/conf/ldscript.arm64
@@ -15,6 +15,12 @@ SECTIONS
*(.gnu.warning)
*(.gnu.linkonce.t*)
} =0x9090
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ }
+
/*
* Align to the the largest page size the kernel could be built for.
* If we don't then building page tables in locore.S could fail as it
@@ -66,7 +72,6 @@ SECTIONS
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init : { *(.init) } =0x9090
- .plt : { *(.plt) }
. = ALIGN(4);
_extab_start = .;
diff --git a/sys/conf/options b/sys/conf/options
index 155fbf8e6c8a..54994503ad65 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -104,7 +104,6 @@ COMPAT_LINUXKPI opt_dontuse.h
COMPILING_LINT opt_global.h
CY_PCI_FASTINTR
DEADLKRES opt_watchdog.h
-EXPERIMENTAL opt_global.h
DIRECTIO
FFCLOCK
FULL_PREEMPTION opt_sched.h
diff --git a/sys/crypto/aesni/aeskeys_i386.S b/sys/crypto/aesni/aeskeys_i386.S
index 770c21dbf938..789d1641c25e 100644
--- a/sys/crypto/aesni/aeskeys_i386.S
+++ b/sys/crypto/aesni/aeskeys_i386.S
@@ -269,3 +269,5 @@ ENTRY(aesni_set_deckey)
retl
.cfi_endproc
END(aesni_set_deckey)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/dev/acpica/acpi_spmc.c b/sys/dev/acpica/acpi_spmc.c
index ca7b3bd95ead..03944800327d 100644
--- a/sys/dev/acpica/acpi_spmc.c
+++ b/sys/dev/acpica/acpi_spmc.c
@@ -200,12 +200,13 @@ acpi_spmc_probe(device_t dev)
}
handle = acpi_get_handle(dev);
- if (handle == NULL)
- return (ENXIO);
+ /* ACPI_ID_PROBE() above cannot succeed without a handle. */
+ MPASS(handle != NULL);
sc = device_get_softc(dev);
+ sc->dev = dev;
- /* Check which sets of DSM's are supported. */
+ /* Check which sets of DSMs are supported. */
sc->dsm_sets = 0;
acpi_spmc_check_dsm_set(sc, handle, &intel_dsm_set);
@@ -226,8 +227,6 @@ acpi_spmc_attach(device_t dev)
{
struct acpi_spmc_softc *sc = device_get_softc(dev);
- sc->dev = dev;
-
sc->handle = acpi_get_handle(dev);
if (sc->handle == NULL)
return (ENXIO);
@@ -286,7 +285,7 @@ acpi_spmc_check_dsm_set(struct acpi_spmc_softc *sc, ACPI_HANDLE handle,
if ((dsms_supported & ~max_dsms) != 0)
device_printf(sc->dev, "DSM set %s supports more DSMs than "
- "expected (%#" PRIx64 " vs %#" PRIx64 ").", dsm_set->name,
+ "expected (%#" PRIx64 " vs %#" PRIx64 ").\n", dsm_set->name,
dsms_supported, max_dsms);
}
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 1099e7a25b0a..7bcac6239253 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -507,6 +507,8 @@ acpi_d_state_to_str(int state)
const char *strs[ACPI_D_STATE_COUNT] = {"D0", "D1", "D2", "D3hot",
"D3cold"};
+ if (state == ACPI_STATE_UNKNOWN)
+ return ("unknown D-state");
MPASS(state >= ACPI_STATE_D0 && state <= ACPI_D_STATES_MAX);
return (strs[state]);
}
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c
index 4a6734e22786..0a701e6fd663 100644
--- a/sys/dev/asmc/asmc.c
+++ b/sys/dev/asmc/asmc.c
@@ -123,6 +123,22 @@ static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS);
static int asmc_wol_sysctl(SYSCTL_HANDLER_ARGS);
+static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *);
+
+#ifdef ASMC_DEBUG
+/* Raw key access */
+static int asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS);
+static int asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS);
+static int asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS);
+static int asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS);
+#endif
+
+/* Voltage/Current/Power/Light sensor support */
+static int asmc_sensor_read(device_t, const char *, int *);
+static int asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS);
+static int asmc_detect_sensors(device_t);
+static int asmc_key_dump_by_index(device_t, int, char *, char *, uint8_t *);
+
struct asmc_model {
const char *smc_model; /* smbios.system.product env var. */
const char *smc_desc; /* driver description */
@@ -334,6 +350,12 @@ static const struct asmc_model asmc_models[] = {
ASMC_MBP115_TEMPS, ASMC_MBP115_TEMPNAMES, ASMC_MBP115_TEMPDESCS
},
+ {
+ "MacBookPro13,1", "Apple SMC MacBook Pro Retina Core i5 (late 2016, 13-inch)",
+ ASMC_SMS_FUNCS_DISABLED, ASMC_FAN_FUNCS2, ASMC_LIGHT_FUNCS,
+ ASMC_MBP131_TEMPS, ASMC_MBP131_TEMPNAMES, ASMC_MBP131_TEMPDESCS
+ },
+
/* The Mac Mini has no SMS */
{
"Macmini1,1", "Apple SMC Mac Mini",
@@ -828,6 +850,43 @@ asmc_attach(device_t dev)
}
}
+#ifdef ASMC_DEBUG
+ /*
+ * Raw SMC key access for debugging.
+ */
+ sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_raw_tree),
+ OID_AUTO, "key",
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ dev, 0, asmc_raw_key_sysctl, "A",
+ "SMC key name (4 chars)");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_raw_tree),
+ OID_AUTO, "value",
+ CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ dev, 0, asmc_raw_value_sysctl, "A",
+ "SMC key value (hex string)");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_raw_tree),
+ OID_AUTO, "len",
+ CTLTYPE_U8 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, 0, asmc_raw_len_sysctl, "CU",
+ "SMC key value length");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_raw_tree),
+ OID_AUTO, "type",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, 0, asmc_raw_type_sysctl, "A",
+ "SMC key type (4 chars)");
+#endif
+
if (model->smc_sms_x == NULL)
goto nosms;
@@ -911,6 +970,16 @@ asmc_detach(device_t dev)
if (sc->sc_kbd_bkl != NULL)
backlight_destroy(sc->sc_kbd_bkl);
+ /* Free sensor key arrays */
+ for (int i = 0; i < sc->sc_voltage_count; i++)
+ free(sc->sc_voltage_sensors[i], M_DEVBUF);
+ for (int i = 0; i < sc->sc_current_count; i++)
+ free(sc->sc_current_sensors[i], M_DEVBUF);
+ for (int i = 0; i < sc->sc_power_count; i++)
+ free(sc->sc_power_sensors[i], M_DEVBUF);
+ for (int i = 0; i < sc->sc_light_count; i++)
+ free(sc->sc_light_sensors[i], M_DEVBUF);
+
if (sc->sc_sms_tq) {
taskqueue_drain(sc->sc_sms_tq, &sc->sc_sms_task);
taskqueue_free(sc->sc_sms_tq);
@@ -1082,6 +1151,12 @@ nosms:
sc->sc_nkeys = 0;
}
+ /*
+ * Auto-detect and register voltage/current/power/ambient sensors.
+ * Scans SMC keys and creates sysctls for detected sensors.
+ */
+ asmc_detect_sensors(dev);
+
out_err:
#ifdef ASMC_DEBUG
asmc_dumpall(dev);
@@ -1202,10 +1277,10 @@ static int
asmc_key_dump(device_t dev, int number)
{
struct asmc_softc *sc = device_get_softc(dev);
- char key[5] = { 0 };
- char type[7] = { 0 };
+ char key[ASMC_KEYLEN + 1] = { 0 };
+ char type[ASMC_KEYINFO_RESPLEN + 1] = { 0 };
uint8_t index[4];
- uint8_t v[32];
+ uint8_t v[ASMC_MAXVAL];
uint8_t maxlen;
int i, error = 1, try = 0;
@@ -1214,40 +1289,40 @@ asmc_key_dump(device_t dev, int number)
index[0] = (number >> 24) & 0xff;
index[1] = (number >> 16) & 0xff;
index[2] = (number >> 8) & 0xff;
- index[3] = (number) & 0xff;
+ index[3] = number & 0xff;
begin:
- if (asmc_command(dev, 0x12))
+ if (asmc_command(dev, ASMC_CMDGETBYINDEX))
goto out;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ASMC_KEYLEN; i++) {
ASMC_DATAPORT_WRITE(sc, index[i]);
- if (asmc_wait(dev, 0x04))
+ if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
goto out;
}
- ASMC_DATAPORT_WRITE(sc, 4);
+ ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
- for (i = 0; i < 4; i++) {
- if (asmc_wait(dev, 0x05))
+ for (i = 0; i < ASMC_KEYLEN; i++) {
+ if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
goto out;
key[i] = ASMC_DATAPORT_READ(sc);
}
- /* get type */
- if (asmc_command(dev, 0x13))
+ /* Get key info (length + type). */
+ if (asmc_command(dev, ASMC_CMDGETINFO))
goto out;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ASMC_KEYLEN; i++) {
ASMC_DATAPORT_WRITE(sc, key[i]);
- if (asmc_wait(dev, 0x04))
+ if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
goto out;
}
- ASMC_DATAPORT_WRITE(sc, 6);
+ ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
- for (i = 0; i < 6; i++) {
- if (asmc_wait(dev, 0x05))
+ for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
+ if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
goto out;
type[i] = ASMC_DATAPORT_READ(sc);
}
@@ -1255,42 +1330,599 @@ begin:
error = 0;
out:
if (error) {
- if (++try < 10)
+ if (++try < ASMC_MAXRETRIES)
goto begin;
- device_printf(dev, "%s for key %s failed %d times, giving up\n",
- __func__, key, try);
- mtx_unlock_spin(&sc->sc_mtx);
+ device_printf(dev,
+ "%s for key %d failed %d times, giving up\n",
+ __func__, number, try);
+ }
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ if (error)
+ return (error);
+
+ maxlen = type[0];
+ type[0] = ' ';
+ type[5] = '\0';
+ if (maxlen > sizeof(v))
+ maxlen = sizeof(v);
+
+ memset(v, 0, sizeof(v));
+ error = asmc_key_read(dev, key, v, maxlen);
+ if (error)
+ return (error);
+
+ device_printf(dev, "key %d: %s, type%s (len %d), data",
+ number, key, type, maxlen);
+ for (i = 0; i < maxlen; i++)
+ printf(" %02x", v[i]);
+ printf("\n");
+
+ return (0);
+}
+#endif /* ASMC_DEBUG */
+
+/*
+ * Get key info (length and type) from SMC using command 0x13.
+ * If len is non-NULL, stores the key's value length.
+ * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes).
+ */
+static int
+asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint8_t info[ASMC_KEYINFO_RESPLEN];
+ int i, error = -1, try = 0;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+begin:
+ if (asmc_command(dev, ASMC_CMDGETINFO))
+ goto out;
+
+ for (i = 0; i < ASMC_KEYLEN; i++) {
+ ASMC_DATAPORT_WRITE(sc, key[i]);
+ if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
+
+ for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
+ if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
+ goto out;
+ info[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ error = 0;
+out:
+ if (error && ++try < ASMC_MAXRETRIES)
+ goto begin;
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ if (error == 0) {
+ if (len != NULL)
+ *len = info[0];
+ if (type != NULL) {
+ for (i = 0; i < ASMC_TYPELEN; i++)
+ type[i] = info[i + 1];
+ type[ASMC_TYPELEN] = '\0';
+ }
+ }
+ return (error);
+}
+
+#ifdef ASMC_DEBUG
+/*
+ * Raw SMC key access sysctls - enables reading/writing any SMC key by name
+ * Usage:
+ * sysctl dev.asmc.0.raw.key=AUPO # Set key, auto-detects length
+ * sysctl dev.asmc.0.raw.value # Read current value (hex bytes)
+ * sysctl dev.asmc.0.raw.value=01 # Write new value
+ */
+static int
+asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ struct asmc_softc *sc = device_get_softc(dev);
+ char newkey[ASMC_KEYLEN + 1];
+ uint8_t keylen;
+ int error;
+
+ strlcpy(newkey, sc->sc_rawkey, sizeof(newkey));
+ error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ if (strlen(newkey) != ASMC_KEYLEN)
+ return (EINVAL);
+
+ /* Get key info to auto-detect length and type */
+ if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0)
+ return (ENOENT);
+
+ if (keylen > ASMC_MAXVAL)
+ keylen = ASMC_MAXVAL;
+
+ strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey));
+ sc->sc_rawlen = keylen;
+ memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
+
+ /* Read the key value */
+ asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen);
+
+ return (0);
+}
+
+static int
+asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ struct asmc_softc *sc = device_get_softc(dev);
+ char hexbuf[ASMC_MAXVAL * 2 + 1];
+ int error, i;
+
+ /* Refresh from SMC if a key has been selected. */
+ if (sc->sc_rawkey[0] != '\0') {
+ asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval,
+ sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL);
+ }
+
+ /* Format as hex string */
+ for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++)
+ snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]);
+ hexbuf[i * 2] = '\0';
+
+ error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ /* Reject writes until a key is selected via raw.key. */
+ if (sc->sc_rawkey[0] == '\0')
+ return (EINVAL);
+
+ memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval));
+ for (i = 0; i < sc->sc_rawlen && hexbuf[i*2] && hexbuf[i*2+1]; i++) {
+ unsigned int val;
+ char tmp[3] = { hexbuf[i*2], hexbuf[i*2+1], 0 };
+ if (sscanf(tmp, "%02x", &val) == 1)
+ sc->sc_rawval[i] = (uint8_t)val;
+ }
+
+ if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen) != 0)
+ return (EIO);
+
+ return (0);
+}
+
+static int
+asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ struct asmc_softc *sc = device_get_softc(dev);
+
+ return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req));
+}
+
+static int
+asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ struct asmc_softc *sc = device_get_softc(dev);
+
+ return (sysctl_handle_string(oidp, sc->sc_rawtype,
+ sizeof(sc->sc_rawtype), req));
+}
+#endif
+
+/*
+ * Convert signed fixed-point SMC values to milli-units.
+ * Format "spXY" means signed with X integer bits and Y fraction bits.
+ */
+static int
+asmc_sp78_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 256;
+}
+
+static int
+asmc_sp87_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 128;
+}
+
+static int
+asmc_sp4b_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 2048;
+}
+
+static int
+asmc_sp5a_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 1024;
+}
+
+static int
+asmc_sp69_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 512;
+}
+
+static int
+asmc_sp96_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 64;
+}
+
+static int
+asmc_sp2d_to_milli(const uint8_t *buf)
+{
+ int16_t val = (int16_t)be16dec(buf);
+
+ return ((int)val * 1000) / 8192;
+}
+
+static bool
+asmc_sensor_type_supported(const char *type)
+{
+
+ return (strncmp(type, "sp78", 4) == 0 ||
+ strncmp(type, "sp87", 4) == 0 ||
+ strncmp(type, "sp4b", 4) == 0 ||
+ strncmp(type, "sp5a", 4) == 0 ||
+ strncmp(type, "sp69", 4) == 0 ||
+ strncmp(type, "sp96", 4) == 0 ||
+ strncmp(type, "sp2d", 4) == 0 ||
+ strncmp(type, "ui16", 4) == 0);
+}
+
+/*
+ * Generic sensor value reader with automatic type conversion.
+ * Reads an SMC key, detects its type, and converts to millivalue.
+ */
+static int
+asmc_sensor_read(device_t dev, const char *key, int *millivalue)
+{
+ uint8_t buf[2];
+ char type[ASMC_TYPELEN + 1];
+ uint8_t len;
+ int error;
+
+ error = asmc_key_getinfo(dev, key, &len, type);
+ if (error != 0)
+ return (error);
+
+ if (len != 2) {
+ if (bootverbose)
+ device_printf(dev,
+ "%s: key %s unexpected length %d\n",
+ __func__, key, len);
+ return (ENXIO);
+ }
+
+ error = asmc_key_read(dev, key, buf, sizeof(buf));
+ if (error != 0)
+ return (error);
+
+ if (strncmp(type, "sp78", 4) == 0) {
+ *millivalue = asmc_sp78_to_milli(buf);
+ } else if (strncmp(type, "sp87", 4) == 0) {
+ *millivalue = asmc_sp87_to_milli(buf);
+ } else if (strncmp(type, "sp4b", 4) == 0) {
+ *millivalue = asmc_sp4b_to_milli(buf);
+ } else if (strncmp(type, "sp5a", 4) == 0) {
+ *millivalue = asmc_sp5a_to_milli(buf);
+ } else if (strncmp(type, "sp69", 4) == 0) {
+ *millivalue = asmc_sp69_to_milli(buf);
+ } else if (strncmp(type, "sp96", 4) == 0) {
+ *millivalue = asmc_sp96_to_milli(buf);
+ } else if (strncmp(type, "sp2d", 4) == 0) {
+ *millivalue = asmc_sp2d_to_milli(buf);
+ } else if (strncmp(type, "ui16", 4) == 0) {
+ *millivalue = be16dec(buf);
} else {
- char buf[1024];
- char buf2[8];
- mtx_unlock_spin(&sc->sc_mtx);
- maxlen = type[0];
- type[0] = ' ';
- type[5] = 0;
- if (maxlen > sizeof(v)) {
+ if (bootverbose)
device_printf(dev,
- "WARNING: cropping maxlen from %d to %zu\n", maxlen,
- sizeof(v));
- maxlen = sizeof(v);
+ "%s: unknown type '%s' for key %s\n",
+ __func__, type, key);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Generic sensor sysctl handler for voltage/current/power/light sensors.
+ * arg2 encodes: sensor_type (high byte) | sensor_index (low byte)
+ * Sensor types: 'V'=voltage, 'I'=current, 'P'=power, 'L'=light
+ */
+static int
+asmc_sensor_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ struct asmc_softc *sc = device_get_softc(dev);
+ int error, val;
+ int sensor_type = (arg2 >> 8) & 0xFF;
+ int sensor_idx = arg2 & 0xFF;
+ const char *key = NULL;
+
+ /* Select sensor based on type and index */
+ switch (sensor_type) {
+ case 'V': /* Voltage */
+ if (sensor_idx < sc->sc_voltage_count)
+ key = sc->sc_voltage_sensors[sensor_idx];
+ break;
+ case 'I': /* Current */
+ if (sensor_idx < sc->sc_current_count)
+ key = sc->sc_current_sensors[sensor_idx];
+ break;
+ case 'P': /* Power */
+ if (sensor_idx < sc->sc_power_count)
+ key = sc->sc_power_sensors[sensor_idx];
+ break;
+ case 'L': /* Light */
+ if (sensor_idx < sc->sc_light_count)
+ key = sc->sc_light_sensors[sensor_idx];
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (key == NULL)
+ return (ENOENT);
+
+ error = asmc_sensor_read(dev, key, &val);
+ if (error != 0)
+ return (error);
+
+ return (sysctl_handle_int(oidp, &val, 0, req));
+}
+
+/*
+ * Detect and register voltage/current/power/ambient sensors.
+ * Scans all SMC keys and identifies sensor keys by prefix.
+ * Returns 0 on success, -1 on error.
+ */
+static int
+asmc_detect_sensors(device_t dev)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *sysctlctx;
+ struct sysctl_oid *tree_node;
+ char key[ASMC_KEYLEN + 1];
+ char type[ASMC_TYPELEN + 1];
+ uint8_t len;
+ unsigned int nkeys;
+ unsigned int i;
+ int error;
+ char *sensor_key;
+
+ /* Initialize counts */
+ sc->sc_voltage_count = 0;
+ sc->sc_current_count = 0;
+ sc->sc_power_count = 0;
+ sc->sc_light_count = 0;
+
+ if (sc->sc_nkeys == 0)
+ return (0);
+ nkeys = sc->sc_nkeys;
+
+ /* Scan all keys for voltage/current/power/ambient light sensors */
+ for (i = 0; i < nkeys; i++) {
+ /* Get key name by index */
+ error = asmc_key_dump_by_index(dev, i, key, type, &len);
+ if (error != 0)
+ continue;
+ if (!asmc_sensor_type_supported(type))
+ continue;
+
+ /* Voltage sensors (VC*, VD*, VG*, VP*, VI*) */
+ if (key[0] == 'V' && (key[1] == 'C' || key[1] == 'D' ||
+ key[1] == 'G' || key[1] == 'P' || key[1] == 'I') &&
+ len == 2) {
+ if (sc->sc_voltage_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(ASMC_KEYLEN + 1,
+ M_DEVBUF, M_WAITOK);
+ memcpy(sensor_key, key, ASMC_KEYLEN + 1);
+ sc->sc_voltage_sensors[sc->sc_voltage_count++] =
+ sensor_key;
+ } else if (key[0] == 'I' && (key[1] == 'C' ||
+ key[1] == 'D' || key[1] == 'G' || key[1] == 'M' ||
+ key[1] == 'N' || key[1] == 'O' || key[1] == 'H' ||
+ key[1] == 'P' || key[1] == 'B' || key[1] == 'A' ||
+ key[1] == 'L') && len == 2) {
+ /* Current sensors */
+ if (sc->sc_current_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(ASMC_KEYLEN + 1,
+ M_DEVBUF, M_WAITOK);
+ memcpy(sensor_key, key, ASMC_KEYLEN + 1);
+ sc->sc_current_sensors[sc->sc_current_count++] =
+ sensor_key;
+ } else if (key[0] == 'P' && (key[1] == 'C' ||
+ key[1] == 'D' || key[1] == 'N' || key[1] == 'S' ||
+ key[1] == 'T' || key[1] == 'H' || key[1] == 'F' ||
+ key[1] == 'Z' || key[1] == 'z') && len == 2) {
+ /* Power sensors */
+ if (sc->sc_power_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(ASMC_KEYLEN + 1,
+ M_DEVBUF, M_WAITOK);
+ memcpy(sensor_key, key, ASMC_KEYLEN + 1);
+ sc->sc_power_sensors[sc->sc_power_count++] =
+ sensor_key;
+ } else if (key[0] == 'A' && key[1] == 'L' &&
+ (key[2] == 'V' || key[2] == 'S') && len == 2) {
+ /* Ambient light sensors */
+ if (sc->sc_light_count >= ASMC_MAX_SENSORS)
+ continue;
+ sensor_key = malloc(ASMC_KEYLEN + 1,
+ M_DEVBUF, M_WAITOK);
+ memcpy(sensor_key, key, ASMC_KEYLEN + 1);
+ sc->sc_light_sensors[sc->sc_light_count++] =
+ sensor_key;
}
- for (i = 0; i < sizeof(v); i++) {
- v[i] = 0;
+ }
+
+ if (bootverbose)
+ device_printf(dev,
+ "detected %d voltage, %d current, "
+ "%d power, %d light sensors\n",
+ sc->sc_voltage_count, sc->sc_current_count,
+ sc->sc_power_count, sc->sc_light_count);
+
+ /* Register sysctls for detected sensors */
+ sysctlctx = device_get_sysctl_ctx(dev);
+
+ /* Voltage sensors */
+ if (sc->sc_voltage_count > 0) {
+ tree_node = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "voltage", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Voltage sensors (millivolts)");
+
+ for (i = 0; i < sc->sc_voltage_count; i++) {
+ SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
+ OID_AUTO, sc->sc_voltage_sensors[i],
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, ('V' << 8) | i, asmc_sensor_sysctl, "I",
+ "Voltage sensor (millivolts)");
}
- asmc_key_read(dev, key, v, maxlen);
- snprintf(buf, sizeof(buf),
- "key %d is: %s, type %s (len %d), data",
- number, key, type, maxlen);
- for (i = 0; i < maxlen; i++) {
- snprintf(buf2, sizeof(buf2), " %02x", v[i]);
- strlcat(buf, buf2, sizeof(buf));
+ }
+
+ /* Current sensors */
+ if (sc->sc_current_count > 0) {
+ tree_node = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "current", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Current sensors (milliamps)");
+
+ for (i = 0; i < sc->sc_current_count; i++) {
+ SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
+ OID_AUTO, sc->sc_current_sensors[i],
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, ('I' << 8) | i, asmc_sensor_sysctl, "I",
+ "Current sensor (milliamps)");
}
- strlcat(buf, " \n", sizeof(buf));
- device_printf(dev, "%s", buf);
}
+ /* Power sensors */
+ if (sc->sc_power_count > 0) {
+ tree_node = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "power", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Power sensors (milliwatts)");
+
+ for (i = 0; i < sc->sc_power_count; i++) {
+ SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
+ OID_AUTO, sc->sc_power_sensors[i],
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, ('P' << 8) | i, asmc_sensor_sysctl, "I",
+ "Power sensor (milliwatts)");
+ }
+ }
+
+ /* Ambient light sensors */
+ if (sc->sc_light_count > 0) {
+ tree_node = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "ambient", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Ambient light sensors");
+
+ for (i = 0; i < sc->sc_light_count; i++) {
+ SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(tree_node),
+ OID_AUTO, sc->sc_light_sensors[i],
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ dev, ('L' << 8) | i, asmc_sensor_sysctl, "I",
+ "Light sensor value");
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Helper function to get key info by index (for sensor detection).
+ */
+static int
+asmc_key_dump_by_index(device_t dev, int index, char *key_out,
+ char *type_out, uint8_t *len_out)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint8_t index_buf[ASMC_KEYLEN];
+ uint8_t key_buf[ASMC_KEYLEN];
+ uint8_t info_buf[ASMC_KEYINFO_RESPLEN];
+ int error = ENXIO, try = 0;
+ int i;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ index_buf[0] = (index >> 24) & 0xff;
+ index_buf[1] = (index >> 16) & 0xff;
+ index_buf[2] = (index >> 8) & 0xff;
+ index_buf[3] = index & 0xff;
+
+begin:
+ if (asmc_command(dev, ASMC_CMDGETBYINDEX))
+ goto out;
+
+ for (i = 0; i < ASMC_KEYLEN; i++) {
+ ASMC_DATAPORT_WRITE(sc, index_buf[i]);
+ if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN);
+
+ for (i = 0; i < ASMC_KEYLEN; i++) {
+ if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
+ goto out;
+ key_buf[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ if (asmc_command(dev, ASMC_CMDGETINFO))
+ goto out;
+
+ for (i = 0; i < ASMC_KEYLEN; i++) {
+ ASMC_DATAPORT_WRITE(sc, key_buf[i]);
+ if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA))
+ goto out;
+ }
+
+ ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN);
+
+ for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) {
+ if (asmc_wait(dev, ASMC_STATUS_DATA_READY))
+ goto out;
+ info_buf[i] = ASMC_DATAPORT_READ(sc);
+ }
+
+ memcpy(key_out, key_buf, ASMC_KEYLEN);
+ key_out[ASMC_KEYLEN] = '\0';
+ *len_out = info_buf[0];
+ memcpy(type_out, &info_buf[1], ASMC_TYPELEN);
+ type_out[ASMC_TYPELEN] = '\0';
+ error = 0;
+
+out:
+ if (error) {
+ if (++try < ASMC_MAXRETRIES)
+ goto begin;
+ }
+
+ mtx_unlock_spin(&sc->sc_mtx);
return (error);
}
-#endif
static int
asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h
index cfc176559ed9..43f679f3fef0 100644
--- a/sys/dev/asmc/asmcvar.h
+++ b/sys/dev/asmc/asmcvar.h
@@ -28,6 +28,10 @@
*/
#define ASMC_MAXFANS 6
+#define ASMC_MAXVAL 32 /* Maximum SMC value size */
+#define ASMC_KEYLEN 4 /* SMC key name length */
+#define ASMC_TYPELEN 4 /* SMC type string length */
+#define ASMC_MAX_SENSORS 64 /* Max sensors per type */
struct asmc_softc {
device_t sc_dev;
@@ -53,6 +57,23 @@ struct asmc_softc {
uint8_t sc_sms_intr_works;
struct cdev *sc_kbd_bkl;
uint32_t sc_kbd_bkl_level;
+#ifdef ASMC_DEBUG
+ /* Raw key access */
+ struct sysctl_oid *sc_raw_tree;
+ char sc_rawkey[ASMC_KEYLEN + 1];
+ uint8_t sc_rawval[ASMC_MAXVAL];
+ uint8_t sc_rawlen;
+ char sc_rawtype[ASMC_TYPELEN + 1];
+#endif
+ /* Voltage/Current/Power/Light sensors */
+ char *sc_voltage_sensors[ASMC_MAX_SENSORS];
+ int sc_voltage_count;
+ char *sc_current_sensors[ASMC_MAX_SENSORS];
+ int sc_current_count;
+ char *sc_power_sensors[ASMC_MAX_SENSORS];
+ int sc_power_count;
+ char *sc_light_sensors[ASMC_MAX_SENSORS];
+ int sc_light_count;
};
/*
@@ -71,6 +92,14 @@ struct asmc_softc {
bus_write_1(sc->sc_ioport, 0x04, val)
#define ASMC_CMDREAD 0x10
#define ASMC_CMDWRITE 0x11
+#define ASMC_CMDGETBYINDEX 0x12
+#define ASMC_CMDGETINFO 0x13
+
+#define ASMC_STATUS_AWAIT_DATA 0x04
+#define ASMC_STATUS_DATA_READY 0x05
+
+#define ASMC_KEYINFO_RESPLEN 6 /* getinfo: 1 len + 4 type + 1 attr */
+#define ASMC_MAXRETRIES 10
/*
* Interrupt port.
@@ -90,7 +119,7 @@ struct asmc_softc {
#define ASMC_KEY_FANMANUAL "FS! " /* RW; 2 bytes */
#define ASMC_KEY_FANID "F%dID" /* RO; 16 bytes */
#define ASMC_KEY_FANSPEED "F%dAc" /* RO; 2 bytes */
-#define ASMC_KEY_FANMINSPEED "F%dMn" /* RO; 2 bytes */
+#define ASMC_KEY_FANMINSPEED "F%dMn" /* RW; 2 bytes */
#define ASMC_KEY_FANMAXSPEED "F%dMx" /* RO; 2 bytes */
#define ASMC_KEY_FANSAFESPEED "F%dSf" /* RO; 2 bytes */
#define ASMC_KEY_FANTARGETSPEED "F%dTg" /* RW; 2 bytes */
@@ -545,6 +574,24 @@ struct asmc_softc {
"Pbus", "Ambient Light", "Leftside", "Rightside", "CPU Package Core", \
"CPU Package GPU", "CPU Package Total", "System Total", "DC In" }
+#define ASMC_MBP131_TEMPS { "TB0T", "TB1T", "TB2T", "TC0F", \
+ "TC0P", "TC1C", "TC2C", "TCGC", \
+ "TCSA", "TCXC", "Th1H", "TM0P", \
+ "TPCD", "Ts0P", "Ts0S", "TaLC", \
+ "Ts1P", NULL }
+
+#define ASMC_MBP131_TEMPNAMES { "battery", "battery_1", "battery_2", "cpu_die_peci", \
+ "cpu_proximity", "cpu_core_1", "cpu_core_2", "intel_gpu", \
+ "cpu_sys_agent", "cpu_core_peci", "right_fin_stack", "memory_proximity", \
+ "platform_ctrl_hub", "trackpad", "bottom_skin", "air_flow", \
+ "trackpad_act" }
+
+#define ASMC_MBP131_TEMPDESCS { "Battery", "Battery Sensor 1", "Battery Sensor 2", "CPU Die (PECI)", \
+ "CPU Proximity", "CPU Core 1", "CPU Core 2", "Intel GPU", \
+ "CPU System Agent Core (PECI)", "CPU Core (PECI)", "Right Fin Stack", "DDR3 Proximity", \
+ "Platform Controller Hub Die", "Trackpad", "Bottom Skin", "Air Flow", \
+ "Trackpad Actuator" }
+
#define ASMC_MM_TEMPS { "TN0P", "TN1P", NULL }
#define ASMC_MM_TEMPNAMES { "northbridge1", "northbridge2" }
#define ASMC_MM_TEMPDESCS { "Northbridge Point 1", \
diff --git a/sys/dev/clk/clk_fixed.c b/sys/dev/clk/clk_fixed.c
index f8dcfb8378cd..6656aeec473e 100644
--- a/sys/dev/clk/clk_fixed.c
+++ b/sys/dev/clk/clk_fixed.c
@@ -157,9 +157,11 @@ clk_fixed_probe(device_t dev)
clk_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
switch (clk_type) {
case CLK_TYPE_FIXED:
- if (OF_hasprop(ofw_bus_get_node(dev), "clock-frequency") == 0) {
- device_printf(dev,
- "clock-fixed has no clock-frequency\n");
+ if (!OF_hasprop(ofw_bus_get_node(dev), "clock-frequency")) {
+ if (bootverbose) {
+ device_printf(dev,
+ "clock-fixed has no clock-frequency\n");
+ }
return (ENXIO);
}
device_set_desc(dev, "Fixed clock");
diff --git a/sys/dev/cxgbe/crypto/t6_kern_tls.c b/sys/dev/cxgbe/crypto/t6_kern_tls.c
index 454b2e264a0e..584e5015acfa 100644
--- a/sys/dev/cxgbe/crypto/t6_kern_tls.c
+++ b/sys/dev/cxgbe/crypto/t6_kern_tls.c
@@ -458,15 +458,15 @@ t6_tls_tag_alloc(if_t ifp, union if_snd_tag_alloc_params *params,
}
inp = params->tls.inp;
+ tp = intotcpcb(inp);
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
error = ECONNRESET;
goto failed;
}
tlsp->inp = inp;
- tp = intotcpcb(inp);
if (tp->t_flags & TF_REQ_TSTMP) {
tlsp->using_timestamps = true;
if ((tp->ts_offset & 0xfffffff) != 0) {
@@ -501,7 +501,7 @@ t6_tls_tag_alloc(if_t ifp, union if_snd_tag_alloc_params *params,
goto failed;
}
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
error = ECONNRESET;
goto failed;
diff --git a/sys/dev/cxgbe/crypto/t7_kern_tls.c b/sys/dev/cxgbe/crypto/t7_kern_tls.c
index d9710b5bd13f..b6078b9b53b6 100644
--- a/sys/dev/cxgbe/crypto/t7_kern_tls.c
+++ b/sys/dev/cxgbe/crypto/t7_kern_tls.c
@@ -246,7 +246,7 @@ t7_tls_tag_alloc(struct ifnet *ifp, union if_snd_tag_alloc_params *params,
inp = params->tls.inp;
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (intotcpcb(inp)->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
error = ECONNRESET;
goto failed;
diff --git a/sys/dev/cxgbe/cxgbei/cxgbei.c b/sys/dev/cxgbe/cxgbei/cxgbei.c
index ccca45f5f761..4b341c9d37b2 100644
--- a/sys/dev/cxgbe/cxgbei/cxgbei.c
+++ b/sys/dev/cxgbe/cxgbei/cxgbei.c
@@ -499,10 +499,11 @@ do_rx_iscsi_ddp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
toep->ofld_rxq->rx_iscsi_ddp_octets += ip->ip_data_len;
}
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
- CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, pdu_len, inp->inp_flags);
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
+ CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, pdu_len, tp->t_flags);
INP_WUNLOCK(inp);
icl_cxgbei_conn_pdu_free(NULL, ip);
toep->ulpcb2 = NULL;
@@ -513,7 +514,6 @@ do_rx_iscsi_ddp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
* T6+ does not report data PDUs received via DDP without F
* set. This can result in gaps in the TCP sequence space.
*/
- tp = intotcpcb(inp);
MPASS(chip_id(sc) >= CHELSIO_T6 || icp->icp_seq == tp->rcv_nxt);
tp->rcv_nxt = icp->icp_seq + pdu_len;
tp->t_rcvtime = ticks;
@@ -652,10 +652,11 @@ do_rx_iscsi_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
toep->ofld_rxq->rx_iscsi_data_digest_errors++;
}
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
- CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, pdu_len, inp->inp_flags);
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
+ CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, pdu_len, tp->t_flags);
INP_WUNLOCK(inp);
icl_cxgbei_conn_pdu_free(NULL, ip);
toep->ulpcb2 = NULL;
@@ -663,8 +664,6 @@ do_rx_iscsi_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
return (0);
}
- tp = intotcpcb(inp);
-
/*
* If icc is NULL, the connection is being closed in
* icl_cxgbei_conn_close(), just drop this data.
diff --git a/sys/dev/cxgbe/cxgbei/icl_cxgbei.c b/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
index d90d7904a8ae..2e7767a0fc27 100644
--- a/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
+++ b/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
@@ -434,6 +434,7 @@ icl_cxgbei_tx_main(void *arg)
struct toepcb *toep = icc->toep;
struct socket *so = ic->ic_socket;
struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct icl_pdu *ip;
struct mbuf *m;
struct mbufq mq;
@@ -476,7 +477,7 @@ icl_cxgbei_tx_main(void *arg)
INP_WLOCK(inp);
ICL_CONN_UNLOCK(ic);
- if (__predict_false(inp->inp_flags & INP_DROPPED) ||
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED) ||
__predict_false((toep->flags & TPF_ATTACHED) == 0)) {
mbufq_drain(&mq);
} else {
@@ -1080,7 +1081,7 @@ icl_cxgbei_conn_handoff(struct icl_conn *ic, int fd)
inp = sotoinpcb(so);
INP_WLOCK(inp);
tp = intotcpcb(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
error = ENOTCONN;
goto out;
@@ -1334,6 +1335,7 @@ icl_cxgbei_conn_task_setup(struct icl_conn *ic, struct icl_pdu *ip,
struct cxgbei_ddp_state *ddp;
struct ppod_reservation *prsv;
struct inpcb *inp;
+ struct tcpcb *tp;
struct mbufq mq;
uint32_t itt;
int rc = 0;
@@ -1421,8 +1423,9 @@ no_ddp:
* detached already.
*/
inp = sotoinpcb(ic->ic_socket);
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if ((inp->inp_flags & INP_DROPPED) != 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) != 0) {
INP_WUNLOCK(inp);
mbufq_drain(&mq);
t4_free_page_pods(prsv);
@@ -1497,6 +1500,7 @@ icl_cxgbei_conn_transfer_setup(struct icl_conn *ic, struct icl_pdu *ip,
struct ppod_reservation *prsv;
struct ctl_sg_entry *sgl, sg_entry;
struct inpcb *inp;
+ struct tcpcb *tp;
struct mbufq mq;
int sg_entries = ctsio->kern_sg_entries;
uint32_t ttt;
@@ -1597,9 +1601,10 @@ no_ddp:
return (ECONNRESET);
}
inp = sotoinpcb(ic->ic_socket);
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
ICL_CONN_UNLOCK(ic);
- if ((inp->inp_flags & INP_DROPPED) != 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) != 0) {
INP_WUNLOCK(inp);
mbufq_drain(&mq);
t4_free_page_pods(prsv);
diff --git a/sys/dev/cxgbe/iw_cxgbe/qp.c b/sys/dev/cxgbe/iw_cxgbe/qp.c
index cbf4bae00a60..372fc5418b91 100644
--- a/sys/dev/cxgbe/iw_cxgbe/qp.c
+++ b/sys/dev/cxgbe/iw_cxgbe/qp.c
@@ -64,7 +64,7 @@ struct cpl_set_tcb_rpl;
#include "iw_cxgbe.h"
#include "user.h"
-static int creds(struct toepcb *toep, struct inpcb *inp, size_t wrsize);
+static int creds(struct toepcb *toep, struct tcpcb *tp, size_t wrsize);
static int max_fr_immd = T4_MAX_FR_IMMD;//SYSCTL parameter later...
static int alloc_ird(struct c4iw_dev *dev, u32 ird)
@@ -1149,7 +1149,7 @@ static void post_terminate(struct c4iw_qp *qhp, struct t4_cqe *err_cqe,
term->ecode = qhp->attr.ecode;
} else
build_term_codes(err_cqe, &term->layer_etype, &term->ecode);
- ret = creds(toep, inp, sizeof(*wqe));
+ ret = creds(toep, tp, sizeof(*wqe));
if (ret) {
free_wrqe(wr);
return;
@@ -1253,8 +1253,7 @@ rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp, struct c4iw_ep *ep)
int ret;
struct wrqe *wr;
struct socket *so = ep->com.so;
- struct inpcb *inp = sotoinpcb(so);
- struct tcpcb *tp = intotcpcb(inp);
+ struct tcpcb *tp = intotcpcb(sotoinpcb(so));
struct toepcb *toep = tp->t_toe;
KASSERT(rhp == qhp->rhp && ep == qhp->ep, ("%s: EDOOFUS", __func__));
@@ -1277,7 +1276,7 @@ rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp, struct c4iw_ep *ep)
c4iw_init_wr_wait(&ep->com.wr_wait);
- ret = creds(toep, inp, sizeof(*wqe));
+ ret = creds(toep, tp, sizeof(*wqe));
if (ret) {
free_wrqe(wr);
return ret;
@@ -1315,14 +1314,14 @@ static void build_rtr_msg(u8 p2p_type, struct fw_ri_init *init)
}
static int
-creds(struct toepcb *toep, struct inpcb *inp, size_t wrsize)
+creds(struct toepcb *toep, struct tcpcb *tp, size_t wrsize)
{
struct ofld_tx_sdesc *txsd;
CTR3(KTR_IW_CXGBE, "%s:creB %p %u", __func__, toep , wrsize);
- INP_WLOCK(inp);
- if ((inp->inp_flags & INP_DROPPED) != 0) {
- INP_WUNLOCK(inp);
+ INP_WLOCK(tptoinpcb(tp));
+ if (tp->t_flags & TF_DISCONNECTED) {
+ INP_WUNLOCK(tptoinpcb(tp));
return (EINVAL);
}
txsd = &toep->txsd[toep->txsd_pidx];
@@ -1336,7 +1335,7 @@ creds(struct toepcb *toep, struct inpcb *inp, size_t wrsize)
if (__predict_false(++toep->txsd_pidx == toep->txsd_total))
toep->txsd_pidx = 0;
toep->txsd_avail--;
- INP_WUNLOCK(inp);
+ INP_WUNLOCK(tptoinpcb(tp));
CTR5(KTR_IW_CXGBE, "%s:creE %p %u %u %u", __func__, toep ,
txsd->tx_credits, toep->tx_credits, toep->txsd_pidx);
return (0);
@@ -1351,8 +1350,7 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
struct c4iw_rdev *rdev = &qhp->rhp->rdev;
struct adapter *sc = rdev->adap;
struct socket *so = ep->com.so;
- struct inpcb *inp = sotoinpcb(so);
- struct tcpcb *tp = intotcpcb(inp);
+ struct tcpcb *tp = intotcpcb(sotoinpcb(so));
struct toepcb *toep = tp->t_toe;
CTR5(KTR_IW_CXGBE, "%s qhp %p qid 0x%x ep %p tid %u", __func__, qhp,
@@ -1416,7 +1414,7 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
c4iw_init_wr_wait(&ep->com.wr_wait);
- ret = creds(toep, inp, sizeof(*wqe));
+ ret = creds(toep, tp, sizeof(*wqe));
if (ret) {
free_wrqe(wr);
free_ird(rhp, qhp->attr.max_ird);
diff --git a/sys/dev/cxgbe/nvmf/nvmf_che.c b/sys/dev/cxgbe/nvmf/nvmf_che.c
index 5c2174b8a40b..afdfc2f1b758 100644
--- a/sys/dev/cxgbe/nvmf/nvmf_che.c
+++ b/sys/dev/cxgbe/nvmf/nvmf_che.c
@@ -555,6 +555,7 @@ che_write_adapter_mem(struct nvmf_che_qpair *qp, uint32_t addr, uint32_t len,
struct toepcb *toep = qp->toep;
struct socket *so = qp->so;
struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct mbufq mq;
int error;
@@ -568,7 +569,7 @@ che_write_adapter_mem(struct nvmf_che_qpair *qp, uint32_t addr, uint32_t len,
goto error;
INP_WLOCK(inp);
- if ((inp->inp_flags & INP_DROPPED) != 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) != 0) {
INP_WUNLOCK(inp);
error = ECONNRESET;
goto error;
@@ -862,12 +863,13 @@ nvmf_che_write_pdu(struct nvmf_che_qpair *qp, struct mbuf *m)
struct epoch_tracker et;
struct socket *so = qp->so;
struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct toepcb *toep = qp->toep;
CURVNET_SET(so->so_vnet);
NET_EPOCH_ENTER(et);
INP_WLOCK(inp);
- if (__predict_false(inp->inp_flags & INP_DROPPED) ||
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED) ||
__predict_false((toep->flags & TPF_ATTACHED) == 0)) {
m_freem(m);
} else {
@@ -2052,10 +2054,11 @@ do_nvmt_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
("%s: payload length mismatch", __func__));
inp = toep->inp;
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- CTR(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, len, inp->inp_flags);
+ if (tp->t_flags & TF_DISCONNECTED) {
+ CTR(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, len, tp->t_flags);
INP_WUNLOCK(inp);
m_freem(m);
return (0);
@@ -2070,7 +2073,6 @@ do_nvmt_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
mbufq_enqueue(&qp->rx_data, m);
SOCKBUF_UNLOCK(&so->so_rcv);
- tp = intotcpcb(inp);
tp->t_rcvtime = ticks;
#ifdef VERBOSE_TRACES
@@ -2092,6 +2094,7 @@ do_nvmt_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
struct nvmf_che_qpair *qp = toep->ulpcb;
struct socket *so = qp->so;
struct inpcb *inp = toep->inp;
+ struct tcpcb *tp = intotcpcb(inp);
u_int hlen __diagused;
bool empty;
@@ -2107,9 +2110,9 @@ do_nvmt_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
("%s: payload length mismatch", __func__));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- CTR(KTR_CXGBE, "%s: tid %u, rx (hlen %u), inp_flags 0x%x",
- __func__, tid, hlen, inp->inp_flags);
+ if (tp->t_flags & TF_DISCONNECTED) {
+ CTR(KTR_CXGBE, "%s: tid %u, rx (hlen %u), t_flags 0x%x",
+ __func__, tid, hlen, tp->t_flags);
INP_WUNLOCK(inp);
m_freem(m);
return (0);
@@ -2505,7 +2508,7 @@ che_allocate_qpair(bool controller, const nvlist_t *nvl)
inp = sotoinpcb(so);
INP_WLOCK(inp);
tp = intotcpcb(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
free(qp->fl_cid_set, M_NVMF_CHE);
free(qp->fl_cids, M_NVMF_CHE);
diff --git a/sys/dev/cxgbe/tom/t4_connect.c b/sys/dev/cxgbe/tom/t4_connect.c
index c236ee060bc2..e5f6053e2cb6 100644
--- a/sys/dev/cxgbe/tom/t4_connect.c
+++ b/sys/dev/cxgbe/tom/t4_connect.c
@@ -78,6 +78,7 @@ do_act_establish(struct sge_iq *iq, const struct rss_header *rss,
u_int atid = G_TID_TID(ntohl(cpl->tos_atid));
struct toepcb *toep = lookup_atid(sc, atid);
struct inpcb *inp = toep->inp;
+ struct tcpcb *tp = intotcpcb(inp);
KASSERT(m == NULL, ("%s: wasn't expecting payload", __func__));
KASSERT(toep->tid == atid, ("%s: toep tid/atid mismatch", __func__));
@@ -95,7 +96,7 @@ do_act_establish(struct sge_iq *iq, const struct rss_header *rss,
toep->ctrlq = &sc->sge.ctrlq[toep->params.ctrlq_idx];
}
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
/* socket closed by the kernel before hw told us it connected */
diff --git a/sys/dev/cxgbe/tom/t4_cpl_io.c b/sys/dev/cxgbe/tom/t4_cpl_io.c
index 7e1c497240c2..6e34d5f54897 100644
--- a/sys/dev/cxgbe/tom/t4_cpl_io.c
+++ b/sys/dev/cxgbe/tom/t4_cpl_io.c
@@ -245,13 +245,13 @@ send_reset(struct adapter *sc, struct toepcb *toep, uint32_t snd_nxt)
struct cpl_abort_req *req;
int tid = toep->tid;
struct inpcb *inp = toep->inp;
- struct tcpcb *tp = intotcpcb(inp); /* don't use if INP_DROPPED */
+ struct tcpcb *tp = intotcpcb(inp);
INP_WLOCK_ASSERT(inp);
CTR6(KTR_CXGBE, "%s: tid %d (%s), toep_flags 0x%x, inp_flags 0x%x%s",
__func__, toep->tid,
- inp->inp_flags & INP_DROPPED ? "inp dropped" :
+ tp->t_flags & TF_DISCONNECTED ? "TCP disconnected" :
tcpstates[tp->t_state],
toep->flags, inp->inp_flags,
toep->flags & TPF_ABORT_SHUTDOWN ?
@@ -273,7 +273,7 @@ send_reset(struct adapter *sc, struct toepcb *toep, uint32_t snd_nxt)
req = wrtod(wr);
INIT_TP_WR_MIT_CPL(req, CPL_ABORT_REQ, tid);
- if (inp->inp_flags & INP_DROPPED)
+ if (tp->t_flags & TF_DISCONNECTED)
req->rsvd0 = htobe32(snd_nxt);
else
req->rsvd0 = htobe32(tp->snd_nxt);
@@ -284,7 +284,7 @@ send_reset(struct adapter *sc, struct toepcb *toep, uint32_t snd_nxt)
* XXX: What's the correct way to tell that the inp hasn't been detached
* from its socket? Should I even be flushing the snd buffer here?
*/
- if ((inp->inp_flags & INP_DROPPED) == 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
struct socket *so = inp->inp_socket;
if (so != NULL) /* because I'm not sure. See comment above */
@@ -1588,8 +1588,8 @@ t4_tod_output(struct toedev *tod, struct tcpcb *tp)
struct toepcb *toep = tp->t_toe;
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("%s: inp %p dropped.", __func__, inp));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("%s: tcpcb %p disconnected", __func__, tp));
KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
t4_push_data(sc, toep, 0);
@@ -1607,8 +1607,8 @@ t4_send_fin(struct toedev *tod, struct tcpcb *tp)
struct toepcb *toep = tp->t_toe;
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("%s: inp %p dropped.", __func__, inp));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("%s: tcpcb %p disconnected", __func__, tp));
KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
toep->flags |= TPF_SEND_FIN;
@@ -1628,8 +1628,8 @@ t4_send_rst(struct toedev *tod, struct tcpcb *tp)
struct toepcb *toep = tp->t_toe;
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("%s: inp %p dropped.", __func__, inp));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("%s: tcpcb %p disconnected", __func__, tp));
KASSERT(toep != NULL, ("%s: toep is NULL", __func__));
/* hmmmm */
@@ -1921,7 +1921,7 @@ do_abort_req(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
}
toep->flags |= TPF_ABORT_SHUTDOWN;
- if ((inp->inp_flags & INP_DROPPED) == 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
struct socket *so = inp->inp_socket;
if (so != NULL)
@@ -2010,17 +2010,16 @@ do_rx_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
m_adj(m, sizeof(*cpl));
len = m->m_pkthdr.len;
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, len, inp->inp_flags);
+ if (tp->t_flags & TF_DISCONNECTED) {
+ CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, len, tp->t_flags);
INP_WUNLOCK(inp);
m_freem(m);
return (0);
}
- tp = intotcpcb(inp);
-
if (__predict_false(ulp_mode(toep) == ULP_MODE_TLS &&
toep->flags & TPF_TLS_RECEIVE)) {
/* Received "raw" data on a TLS socket. */
@@ -2170,6 +2169,7 @@ do_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
}
inp = toep->inp;
+ tp = intotcpcb(inp);
KASSERT(opcode == CPL_FW4_ACK,
("%s: unexpected opcode 0x%x", __func__, opcode));
@@ -2183,10 +2183,8 @@ do_fw4_ack(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
return (0);
}
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("%s: inp_flags 0x%x", __func__, inp->inp_flags));
-
- tp = intotcpcb(inp);
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("%s: t_flags 0x%x", __func__, tp->t_flags));
if (cpl->flags & CPL_FW4_ACK_FLAGS_SEQVAL) {
tcp_seq snd_una = be32toh(cpl->snd_una);
@@ -2627,8 +2625,9 @@ sendanother:
/* Inlined tcp_usr_send(). */
inp = toep->inp;
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
SOCK_IO_SEND_UNLOCK(so);
error = ECONNRESET;
@@ -2642,8 +2641,7 @@ sendanother:
sbappendstream(sb, m, 0);
m = NULL;
- if (!(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
if (moretocome)
tp->t_flags |= TF_MORETOCOME;
error = tcp_output(tp);
diff --git a/sys/dev/cxgbe/tom/t4_ddp.c b/sys/dev/cxgbe/tom/t4_ddp.c
index 35fb1061d867..9d422c2b793e 100644
--- a/sys/dev/cxgbe/tom/t4_ddp.c
+++ b/sys/dev/cxgbe/tom/t4_ddp.c
@@ -641,8 +641,8 @@ handle_ddp_data_aio(struct toepcb *toep, __be32 ddp_report, __be32 rcv_nxt,
uint32_t report = be32toh(ddp_report);
unsigned int db_idx;
struct inpcb *inp = toep->inp;
+ struct tcpcb *tp = intotcpcb(inp);
struct ddp_buffer *db;
- struct tcpcb *tp;
struct socket *so;
struct sockbuf *sb;
struct kaiocb *job;
@@ -664,13 +664,13 @@ handle_ddp_data_aio(struct toepcb *toep, __be32 ddp_report, __be32 rcv_nxt,
db = &toep->ddp.db[db_idx];
job = db->job;
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
/*
* This can happen due to an administrative tcpdrop(8).
* Just fail the request with ECONNRESET.
*/
- CTR5(KTR_CXGBE, "%s: tid %u, seq 0x%x, len %d, inp_flags 0x%x",
- __func__, toep->tid, be32toh(rcv_nxt), len, inp->inp_flags);
+ CTR5(KTR_CXGBE, "%s: tid %u, seq 0x%x, len %d, t_flags 0x%x",
+ __func__, toep->tid, be32toh(rcv_nxt), len, tp->t_flags);
if (aio_clear_cancel_function(job))
ddp_complete_one(job, ECONNRESET);
goto completed;
@@ -859,7 +859,7 @@ handle_ddp_data_rcvbuf(struct toepcb *toep, __be32 ddp_report, __be32 rcv_nxt,
{
uint32_t report = be32toh(ddp_report);
struct inpcb *inp = toep->inp;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
struct socket *so;
struct sockbuf *sb;
struct ddp_buffer *db;
@@ -881,20 +881,18 @@ handle_ddp_data_rcvbuf(struct toepcb *toep, __be32 ddp_report, __be32 rcv_nxt,
toep->ddp.active_id, toep->tid));
db = &toep->ddp.db[db_idx];
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
/*
* This can happen due to an administrative tcpdrop(8).
* Just ignore the received data.
*/
- CTR5(KTR_CXGBE, "%s: tid %u, seq 0x%x, len %d, inp_flags 0x%x",
- __func__, toep->tid, be32toh(rcv_nxt), len, inp->inp_flags);
+ CTR5(KTR_CXGBE, "%s: tid %u, seq 0x%x, len %d, t_flags 0x%x",
+ __func__, toep->tid, be32toh(rcv_nxt), len, tp->t_flags);
if (invalidated)
complete_ddp_buffer(toep, db, db_idx);
goto out;
}
- tp = intotcpcb(inp);
-
/*
* For RX_DDP_COMPLETE, len will be zero and rcv_nxt is the
* sequence number of the next byte to receive. The length of
diff --git a/sys/dev/cxgbe/tom/t4_listen.c b/sys/dev/cxgbe/tom/t4_listen.c
index b879f6883f25..359267b7db90 100644
--- a/sys/dev/cxgbe/tom/t4_listen.c
+++ b/sys/dev/cxgbe/tom/t4_listen.c
@@ -886,6 +886,7 @@ do_pass_open_rpl(struct sge_iq *iq, const struct rss_header *rss,
unsigned int status = cpl->status;
struct listen_ctx *lctx = lookup_stid(sc, stid);
struct inpcb *inp = lctx->inp;
+ struct tcpcb *tp = intotcpcb(inp);
#ifdef INVARIANTS
unsigned int opcode = G_CPL_OPCODE(be32toh(OPCODE_TID(cpl)));
#endif
@@ -911,13 +912,13 @@ do_pass_open_rpl(struct sge_iq *iq, const struct rss_header *rss,
* If the inp has been dropped (listening socket closed) then
* listen_stop must have run and taken the inp out of the hash.
*/
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
KASSERT(listen_hash_del(sc, inp) == NULL,
("%s: inp %p still in listen hash", __func__, inp));
}
#endif
- if (inp->inp_flags & INP_DROPPED && status != CPL_ERR_NONE) {
+ if (tp->t_flags & TF_DISCONNECTED && status != CPL_ERR_NONE) {
if (release_lctx(sc, lctx) != NULL)
INP_WUNLOCK(inp);
return (status);
@@ -928,7 +929,7 @@ do_pass_open_rpl(struct sge_iq *iq, const struct rss_header *rss,
* it has started the hardware listener. Stop it; the lctx will be
* released in do_close_server_rpl.
*/
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
destroy_server(sc, lctx);
INP_WUNLOCK(inp);
return (status);
@@ -1336,6 +1337,7 @@ do_pass_accept_req(struct sge_iq *iq, const struct rss_header *rss,
unsigned int tid = GET_TID(cpl);
struct listen_ctx *lctx = lookup_stid(sc, stid);
struct inpcb *inp;
+ struct tcpcb *tp;
struct socket *so;
struct in_conninfo inc;
struct tcphdr th;
@@ -1477,10 +1479,11 @@ found:
}
inp = lctx->inp; /* listening socket, not owned by TOE */
+ tp = intotcpcb(inp);
INP_RLOCK(inp);
/* Don't offload if the listening socket has closed */
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_RUNLOCK(inp);
NET_EPOCH_EXIT(et);
REJECT_PASS_ACCEPT_REQ(false);
@@ -1622,6 +1625,7 @@ do_pass_establish(struct sge_iq *iq, const struct rss_header *rss,
struct synq_entry *synqe = lookup_tid(sc, tid);
struct listen_ctx *lctx = synqe->lctx;
struct inpcb *inp = lctx->inp, *new_inp;
+ struct tcpcb *tp = intotcpcb(inp);
struct socket *so;
struct tcphdr th;
struct tcpopt to;
@@ -1653,7 +1657,7 @@ do_pass_establish(struct sge_iq *iq, const struct rss_header *rss,
KASSERT(vi->adapter == sc,
("%s: vi %p, sc %p mismatch", __func__, vi, sc));
- if (__predict_false(inp->inp_flags & INP_DROPPED)) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
reset:
send_abort_rpl_synqe(TOEDEV(ifp), synqe, CPL_ABORT_SEND_RST);
INP_WUNLOCK(inp);
diff --git a/sys/dev/cxgbe/tom/t4_tls.c b/sys/dev/cxgbe/tom/t4_tls.c
index bbcc1c88c3db..0616279ba15e 100644
--- a/sys/dev/cxgbe/tom/t4_tls.c
+++ b/sys/dev/cxgbe/tom/t4_tls.c
@@ -762,7 +762,7 @@ do_tls_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
unsigned int tid = GET_TID(cpl);
struct toepcb *toep = lookup_tid(sc, tid);
struct inpcb *inp = toep->inp;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
int len;
/* XXX: Should this match do_rx_data instead? */
@@ -781,9 +781,9 @@ do_tls_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
("%s: payload length mismatch", __func__));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, len, inp->inp_flags);
+ if (tp->t_flags & TF_DISCONNECTED) {
+ CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, len, tp->t_flags);
INP_WUNLOCK(inp);
m_freem(m);
return (0);
@@ -803,7 +803,6 @@ do_tls_data(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
#endif
}
- tp = intotcpcb(inp);
tp->t_rcvtime = ticks;
#ifdef VERBOSE_TRACES
@@ -824,7 +823,7 @@ do_rx_tls_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
unsigned int tid = GET_TID(cpl);
struct toepcb *toep = lookup_tid(sc, tid);
struct inpcb *inp = toep->inp;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
struct socket *so;
struct sockbuf *sb;
struct mbuf *tls_data;
@@ -851,9 +850,9 @@ do_rx_tls_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
("%s: payload length mismatch", __func__));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), inp_flags 0x%x",
- __func__, tid, len, inp->inp_flags);
+ if (tp->t_flags & TF_DISCONNECTED) {
+ CTR4(KTR_CXGBE, "%s: tid %u, rx (%d bytes), t_flags 0x%x",
+ __func__, tid, len, tp->t_flags);
INP_WUNLOCK(inp);
m_freem(m);
return (0);
@@ -862,7 +861,6 @@ do_rx_tls_cmp(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
pdu_length = G_CPL_RX_TLS_CMP_PDULENGTH(be32toh(cpl->pdulength_length));
so = inp_inpcbtosocket(inp);
- tp = intotcpcb(inp);
#ifdef VERBOSE_TRACES
CTR6(KTR_CXGBE, "%s: tid %u PDU len %d len %d seq %u, rcv_nxt %u",
diff --git a/sys/dev/cxgbe/tom/t4_tom.c b/sys/dev/cxgbe/tom/t4_tom.c
index 8dfffd465345..950608053be7 100644
--- a/sys/dev/cxgbe/tom/t4_tom.c
+++ b/sys/dev/cxgbe/tom/t4_tom.c
@@ -1830,7 +1830,7 @@ live_tid_failure_cleanup(struct adapter *sc, struct toepcb *toep, u_int status)
INP_WLOCK(inp);
tp = intotcpcb(inp);
toep->flags |= TPF_ABORT_SHUTDOWN;
- if ((inp->inp_flags & INP_DROPPED) == 0) {
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
struct socket *so = inp->inp_socket;
if (so != NULL)
@@ -2283,8 +2283,8 @@ find_offload_adapter_cb(struct adapter *sc, void *arg)
struct find_offload_adapter_data *fa = arg;
struct socket *so = fa->so;
struct tom_data *td = sc->tom_softc;
- struct tcpcb *tp;
- struct inpcb *inp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
/* Non-TCP were filtered out earlier. */
MPASS(so->so_proto->pr_protocol == IPPROTO_TCP);
@@ -2295,10 +2295,8 @@ find_offload_adapter_cb(struct adapter *sc, void *arg)
if (td == NULL)
return; /* TOE not enabled on this adapter. */
- inp = sotoinpcb(so);
INP_WLOCK(inp);
- if ((inp->inp_flags & INP_DROPPED) == 0) {
- tp = intotcpcb(inp);
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
if (tp->t_flags & TF_TOE && tp->tod == &td->tod)
fa->sc = sc; /* Found. */
}
diff --git a/sys/dev/dwc/if_dwc.c b/sys/dev/dwc/if_dwc.c
index 21a520db8b89..cd8651cc99ff 100644
--- a/sys/dev/dwc/if_dwc.c
+++ b/sys/dev/dwc/if_dwc.c
@@ -527,13 +527,13 @@ dwc_attach(device_t dev)
sc->txpbl = pbl;
if (OF_getencprop(sc->node, "snps,rxpbl", &sc->rxpbl, sizeof(uint32_t)) <= 0)
sc->rxpbl = pbl;
- if (OF_hasprop(sc->node, "snps,no-pbl-x8") == 1)
+ if (OF_hasprop(sc->node, "snps,no-pbl-x8"))
sc->nopblx8 = true;
- if (OF_hasprop(sc->node, "snps,fixed-burst") == 1)
+ if (OF_hasprop(sc->node, "snps,fixed-burst"))
sc->fixed_burst = true;
- if (OF_hasprop(sc->node, "snps,mixed-burst") == 1)
+ if (OF_hasprop(sc->node, "snps,mixed-burst"))
sc->mixed_burst = true;
- if (OF_hasprop(sc->node, "snps,aal") == 1)
+ if (OF_hasprop(sc->node, "snps,aal"))
sc->aal = true;
error = clk_set_assigned(dev, ofw_bus_get_node(dev));
diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c
index 7e9193f4ba47..248a13952d35 100644
--- a/sys/dev/etherswitch/e6000sw/e6000sw.c
+++ b/sys/dev/etherswitch/e6000sw/e6000sw.c
@@ -302,6 +302,10 @@ e6000sw_probe(device_t dev)
description = "Marvell 88E6352";
sc->num_ports = 7;
break;
+ case MV88E6171:
+ description = "Marvell 88E6171";
+ sc->num_ports = 7;
+ break;
case MV88E6172:
description = "Marvell 88E6172";
sc->num_ports = 7;
@@ -571,6 +575,8 @@ e6000sw_attach(device_t dev)
}
for (child = OF_child(ports); child != 0; child = OF_peer(child)) {
+ if (!ofw_bus_node_status_okay(child))
+ continue;
err = e6000sw_parse_child_fdt(sc, child, &port);
if (err != 0) {
device_printf(sc->dev, "failed to parse DTS\n");
diff --git a/sys/dev/etherswitch/e6000sw/e6000swreg.h b/sys/dev/etherswitch/e6000sw/e6000swreg.h
index ec4503faeec5..aef79ad9de5d 100644
--- a/sys/dev/etherswitch/e6000sw/e6000swreg.h
+++ b/sys/dev/etherswitch/e6000sw/e6000swreg.h
@@ -45,6 +45,7 @@ struct atu_opt {
#define MV88E6341 0x3410
#define MV88E6352 0x3520
#define MV88E6172 0x1720
+#define MV88E6171 0x1710
#define MV88E6176 0x1760
#define MV88E6190 0x1900
#define MV88E6190X 0x0a00
diff --git a/sys/dev/hid/appleir.c b/sys/dev/hid/appleir.c
new file mode 100644
index 000000000000..956ad27f6d70
--- /dev/null
+++ b/sys/dev/hid/appleir.c
@@ -0,0 +1,440 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ */
+
+/*
+ * Apple IR Remote Control Driver
+ *
+ * HID driver for Apple IR receivers (USB HID, vendor 0x05ac).
+ * Supports Apple Remote and generic IR remotes using NEC protocol.
+ *
+ * The Apple Remote protocol was reverse-engineered by James McKenzie and
+ * others; key codes and packet format constants are derived from that work
+ * and are factual descriptions of the hardware protocol, not copied code.
+ * Linux reference (GPL-2.0, no code copied): drivers/hid/hid-appleir.c
+ *
+ * Apple Remote Protocol (proprietary):
+ * Key down: [0x25][0x87][0xee][remote_id][key_code]
+ * Key repeat: [0x26][0x87][0xee][remote_id][key_code]
+ * Battery low: [0x25][0x87][0xe0][remote_id][0x00]
+ * Key decode: (byte4 >> 1) & 0x0F -> keymap[index]
+ * Two-packet: bit 6 of key_code (0x40) set -> store index, use on next keydown
+ *
+ * Generic IR Protocol (NEC-style):
+ * Format: [0x26][0x7f][0x80][code][~code]
+ * Checksum: code + ~code = 0xFF
+ *
+ * NO hardware key-up events -- synthesize via 125ms callout timer.
+ */
+
+#include <sys/cdefs.h>
+
+#include "opt_hid.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#define HID_DEBUG_VAR appleir_debug
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include "usbdevs.h"
+
+#ifdef HID_DEBUG
+static int appleir_debug = 0;
+
+static SYSCTL_NODE(_hw_hid, OID_AUTO, appleir, CTLFLAG_RW, 0,
+ "Apple IR Remote Control");
+SYSCTL_INT(_hw_hid_appleir, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &appleir_debug, 0, "Debug level");
+#endif
+
+/* Protocol constants */
+#define APPLEIR_REPORT_LEN 5
+#define APPLEIR_KEY_MASK 0x0F
+#define APPLEIR_TWO_PKT_FLAG 0x40 /* bit 6: two-packet command */
+#define APPLEIR_KEYUP_TICKS MAX(1, hz / 8) /* 125ms */
+#define APPLEIR_TWOPKT_TICKS MAX(1, hz / 4) /* 250ms */
+
+/* Report type markers (byte 0) */
+#define APPLEIR_PKT_KEYDOWN 0x25 /* key down / battery low */
+#define APPLEIR_PKT_REPEAT 0x26 /* key repeat / NEC generic */
+
+/* Apple Remote signature (bytes 1-2) */
+#define APPLEIR_SIG_HI 0x87
+#define APPLEIR_SIG_KEYLO 0xee /* normal key event */
+#define APPLEIR_SIG_BATTLO 0xe0 /* battery low event */
+
+/* Generic IR NEC signature (bytes 1-2) */
+#define APPLEIR_NEC_HI 0x7f
+#define APPLEIR_NEC_LO 0x80
+#define APPLEIR_NEC_CHECKSUM 0xFF /* code + ~code must equal this */
+
+/*
+ * Apple IR keymap: 17 entries, index = (key_code >> 1) & 0x0F
+ * Based on Linux driver (hid-appleir.c) keymap.
+ */
+static const uint16_t appleir_keymap[] = {
+ KEY_RESERVED, /* 0x00 */
+ KEY_MENU, /* 0x01 - menu */
+ KEY_PLAYPAUSE, /* 0x02 - play/pause */
+ KEY_FORWARD, /* 0x03 - >> */
+ KEY_BACK, /* 0x04 - << */
+ KEY_VOLUMEUP, /* 0x05 - + */
+ KEY_VOLUMEDOWN, /* 0x06 - - */
+ KEY_RESERVED, /* 0x07 */
+ KEY_RESERVED, /* 0x08 */
+ KEY_RESERVED, /* 0x09 */
+ KEY_RESERVED, /* 0x0A */
+ KEY_RESERVED, /* 0x0B */
+ KEY_RESERVED, /* 0x0C */
+ KEY_RESERVED, /* 0x0D */
+ KEY_ENTER, /* 0x0E - middle button (two-packet) */
+ KEY_PLAYPAUSE, /* 0x0F - play/pause (two-packet) */
+ KEY_RESERVED, /* 0x10 - out of range guard */
+};
+#define APPLEIR_NKEYS (nitems(appleir_keymap))
+
+/*
+ * Generic IR keymap (NEC protocol codes).
+ * Maps raw NEC codes to evdev KEY_* codes.
+ */
+struct generic_ir_map {
+ uint8_t code; /* NEC IR code */
+ uint16_t key; /* evdev KEY_* */
+};
+
+static const struct generic_ir_map generic_keymap[] = {
+ { 0xe1, KEY_VOLUMEUP },
+ { 0xe9, KEY_VOLUMEDOWN },
+ { 0xed, KEY_CHANNELUP },
+ { 0xf3, KEY_CHANNELDOWN },
+ { 0xf5, KEY_PLAYPAUSE },
+ { 0xf9, KEY_POWER },
+ { 0xfb, KEY_MUTE },
+ { 0xfe, KEY_OK },
+};
+#define GENERIC_NKEYS (nitems(generic_keymap))
+
+static uint16_t
+generic_ir_lookup(uint8_t code)
+{
+ int i;
+
+ for (i = 0; i < GENERIC_NKEYS; i++) {
+ if (generic_keymap[i].code == code)
+ return (generic_keymap[i].key);
+ }
+ return (KEY_RESERVED);
+}
+
+struct appleir_softc {
+ device_t sc_dev;
+ struct mtx sc_mtx; /* protects below + callout */
+ struct evdev_dev *sc_evdev;
+ struct callout sc_co; /* key-up timer */
+ struct callout sc_twoco; /* two-packet timeout */
+ uint16_t sc_current_key; /* evdev keycode (0=none) */
+ int sc_prev_key_idx;/* two-packet state (0=none) */
+ bool sc_batt_warned;
+};
+
+
+/*
+ * Callout: synthesize key-up event (no hardware key-up from remote).
+ * Runs with sc_mtx held (callout_init_mtx).
+ */
+static void
+appleir_keyup(void *arg)
+{
+ struct appleir_softc *sc = arg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ if (sc->sc_current_key != 0) {
+ evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
+ evdev_sync(sc->sc_evdev);
+ sc->sc_current_key = 0;
+ sc->sc_prev_key_idx = 0;
+ }
+}
+
+static void
+appleir_twopacket_timeout(void *arg)
+{
+ struct appleir_softc *sc = arg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ sc->sc_prev_key_idx = 0;
+}
+
+/*
+ * Process 5-byte HID interrupt report.
+ * Called from hidbus interrupt context.
+ */
+static void
+appleir_intr(void *context, void *data, hid_size_t len)
+{
+ struct appleir_softc *sc = context;
+ uint8_t *buf = data;
+ uint8_t report[APPLEIR_REPORT_LEN];
+ int index;
+ uint16_t new_key;
+
+ if (len != APPLEIR_REPORT_LEN) {
+ DPRINTFN(1, "bad report len: %zu\n", (size_t)len);
+ return;
+ }
+
+ memcpy(report, buf, APPLEIR_REPORT_LEN);
+
+ mtx_lock(&sc->sc_mtx);
+
+ /* Battery low: [KEYDOWN][SIG_HI][SIG_BATTLO] -- log and ignore */
+ if (report[0] == APPLEIR_PKT_KEYDOWN &&
+ report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_BATTLO) {
+ if (!sc->sc_batt_warned) {
+ device_printf(sc->sc_dev,
+ "remote battery may be low\n");
+ sc->sc_batt_warned = true;
+ }
+ goto done;
+ }
+
+ /* Key down: [KEYDOWN][SIG_HI][SIG_KEYLO][remote_id][key_code] */
+ if (report[0] == APPLEIR_PKT_KEYDOWN &&
+ report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
+ /* Release previous key if held */
+ if (sc->sc_current_key != 0) {
+ evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
+ evdev_sync(sc->sc_evdev);
+ sc->sc_current_key = 0;
+ }
+
+ if (sc->sc_prev_key_idx > 0) {
+ /* Second packet of a two-packet command */
+ index = sc->sc_prev_key_idx;
+ sc->sc_prev_key_idx = 0;
+ callout_stop(&sc->sc_twoco);
+ } else if (report[4] & APPLEIR_TWO_PKT_FLAG) {
+ /* First packet of a two-packet command -- wait for next */
+ sc->sc_prev_key_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
+ callout_reset(&sc->sc_twoco, APPLEIR_TWOPKT_TICKS,
+ appleir_twopacket_timeout, sc);
+ goto done;
+ } else {
+ index = (report[4] >> 1) & APPLEIR_KEY_MASK;
+ }
+
+ new_key = (index < APPLEIR_NKEYS) ?
+ appleir_keymap[index] : KEY_RESERVED;
+ if (new_key != KEY_RESERVED) {
+ sc->sc_current_key = new_key;
+ evdev_push_key(sc->sc_evdev, new_key, 1);
+ evdev_sync(sc->sc_evdev);
+ callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
+ appleir_keyup, sc);
+ }
+ goto done;
+ }
+
+ /* Key repeat: [REPEAT][SIG_HI][SIG_KEYLO][remote_id][key_code] */
+ if (report[0] == APPLEIR_PKT_REPEAT &&
+ report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
+ uint16_t repeat_key;
+ int repeat_idx;
+
+ if (sc->sc_prev_key_idx > 0)
+ goto done;
+ if (report[4] & APPLEIR_TWO_PKT_FLAG)
+ goto done;
+
+ repeat_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
+ repeat_key = (repeat_idx < APPLEIR_NKEYS) ?
+ appleir_keymap[repeat_idx] : KEY_RESERVED;
+ if (repeat_key == KEY_RESERVED ||
+ repeat_key != sc->sc_current_key)
+ goto done;
+
+ evdev_push_key(sc->sc_evdev, repeat_key, 1);
+ evdev_sync(sc->sc_evdev);
+ callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
+ appleir_keyup, sc);
+ goto done;
+ }
+
+ /* Generic IR (NEC protocol): [REPEAT][NEC_HI][NEC_LO][code][~code] */
+ if (report[0] == APPLEIR_PKT_REPEAT &&
+ report[1] == APPLEIR_NEC_HI && report[2] == APPLEIR_NEC_LO) {
+ uint8_t code = report[3];
+ uint8_t checksum = report[4];
+
+ sc->sc_prev_key_idx = 0;
+ callout_stop(&sc->sc_twoco);
+
+ if ((uint8_t)(code + checksum) != APPLEIR_NEC_CHECKSUM) {
+ DPRINTFN(1, "generic IR: bad checksum %02x+%02x\n",
+ code, checksum);
+ goto done;
+ }
+
+ new_key = generic_ir_lookup(code);
+ if (new_key == KEY_RESERVED)
+ goto done;
+
+ if (sc->sc_current_key != new_key) {
+ if (sc->sc_current_key != 0)
+ evdev_push_key(sc->sc_evdev,
+ sc->sc_current_key, 0);
+ sc->sc_current_key = new_key;
+ evdev_push_key(sc->sc_evdev, new_key, 1);
+ evdev_sync(sc->sc_evdev);
+ } else {
+ evdev_push_key(sc->sc_evdev, new_key, 1);
+ evdev_sync(sc->sc_evdev);
+ }
+ callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
+ appleir_keyup, sc);
+ goto done;
+ }
+
+ DPRINTFN(1, "unknown report: %02x %02x %02x\n",
+ report[0], report[1], report[2]);
+
+done:
+ mtx_unlock(&sc->sc_mtx);
+}
+
+/* Apple IR receiver device IDs */
+static const struct hid_device_id appleir_devs[] = {
+ { HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8240) },
+ { HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8241) },
+ { HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8242) },
+ { HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8243) },
+ { HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x1440) },
+};
+
+static int
+appleir_probe(device_t dev)
+{
+ int error;
+
+ error = HIDBUS_LOOKUP_DRIVER_INFO(dev, appleir_devs);
+ if (error != 0)
+ return (error);
+
+ /* Only attach to first top-level collection (TLC index 0) */
+ if (hidbus_get_index(dev) != 0)
+ return (ENXIO);
+
+ hidbus_set_desc(dev, "Apple IR Receiver");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+appleir_attach(device_t dev)
+{
+ struct appleir_softc *sc = device_get_softc(dev);
+ const struct hid_device_info *hw;
+ int i, error;
+
+ sc->sc_dev = dev;
+ hw = hid_get_device_info(dev);
+ sc->sc_current_key = 0;
+ sc->sc_prev_key_idx = 0;
+ sc->sc_batt_warned = false;
+ mtx_init(&sc->sc_mtx, "appleir", NULL, MTX_DEF);
+ callout_init_mtx(&sc->sc_co, &sc->sc_mtx, 0);
+ callout_init_mtx(&sc->sc_twoco, &sc->sc_mtx, 0);
+
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
+ evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
+ hw->idVersion);
+ evdev_set_serial(sc->sc_evdev, hw->serial);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_KEY);
+ evdev_support_event(sc->sc_evdev, EV_REP);
+
+ for (i = 0; i < APPLEIR_NKEYS; i++) {
+ if (appleir_keymap[i] != KEY_RESERVED)
+ evdev_support_key(sc->sc_evdev, appleir_keymap[i]);
+ }
+ for (i = 0; i < GENERIC_NKEYS; i++)
+ evdev_support_key(sc->sc_evdev, generic_keymap[i].key);
+
+ error = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
+ if (error != 0) {
+ device_printf(dev, "evdev_register_mtx failed: %d\n", error);
+ goto fail;
+ }
+
+ hidbus_set_intr(dev, appleir_intr, sc);
+
+ error = hid_intr_start(dev);
+ if (error != 0) {
+ device_printf(dev, "hid_intr_start failed: %d\n", error);
+ goto fail;
+ }
+
+ return (0);
+
+fail:
+ if (sc->sc_evdev != NULL)
+ evdev_free(sc->sc_evdev);
+ callout_drain(&sc->sc_co);
+ callout_drain(&sc->sc_twoco);
+ mtx_destroy(&sc->sc_mtx);
+ return (error);
+}
+
+static int
+appleir_detach(device_t dev)
+{
+ struct appleir_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = hid_intr_stop(dev);
+ if (error != 0) {
+ device_printf(dev, "hid_intr_stop failed: %d\n", error);
+ return (error);
+ }
+ callout_drain(&sc->sc_co);
+ callout_drain(&sc->sc_twoco);
+ evdev_free(sc->sc_evdev);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static device_method_t appleir_methods[] = {
+ DEVMETHOD(device_probe, appleir_probe),
+ DEVMETHOD(device_attach, appleir_attach),
+ DEVMETHOD(device_detach, appleir_detach),
+ DEVMETHOD_END
+};
+
+static driver_t appleir_driver = {
+ "appleir",
+ appleir_methods,
+ sizeof(struct appleir_softc)
+};
+
+DRIVER_MODULE(appleir, hidbus, appleir_driver, NULL, NULL);
+MODULE_DEPEND(appleir, hid, 1, 1, 1);
+MODULE_DEPEND(appleir, hidbus, 1, 1, 1);
+MODULE_DEPEND(appleir, evdev, 1, 1, 1);
+MODULE_VERSION(appleir, 1);
+HID_PNP_INFO(appleir_devs);
diff --git a/sys/dev/hid/bcm5974.c b/sys/dev/hid/bcm5974.c
index 442e8905e9bc..e2efeb08eb2e 100644
--- a/sys/dev/hid/bcm5974.c
+++ b/sys/dev/hid/bcm5974.c
@@ -120,6 +120,7 @@ enum tp_type {
/* list of device capability bits */
#define HAS_INTEGRATED_BUTTON 1
#define USES_COMPACT_REPORT 2
+#define SUPPORTS_FORCETOUCH 4
struct tp_type_params {
uint8_t caps; /* device capability bitmask */
@@ -146,13 +147,13 @@ struct tp_type_params {
.delta = 0,
},
[TYPE4] = {
- .caps = HAS_INTEGRATED_BUTTON,
+ .caps = HAS_INTEGRATED_BUTTON | SUPPORTS_FORCETOUCH,
.button = 31,
.offset = 23 * 2,
.delta = 2,
},
[TYPE_MT2U] = {
- .caps = HAS_INTEGRATED_BUTTON | USES_COMPACT_REPORT,
+ .caps = HAS_INTEGRATED_BUTTON | USES_COMPACT_REPORT | SUPPORTS_FORCETOUCH,
.button = 1,
.offset = 12,
.delta = 0,
@@ -407,6 +408,10 @@ static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), \
HID_TLC(BCM5974_TLC_PAGE, BCM5974_TLC_USAGE), \
}
+#define BCM5974_DEV_USB(v,p,i) { \
+ HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), \
+ HID_TLC(HUP_VENDOR_00, 0x0001), \
+}
#define APPLE_HID "APP000D"
#define BCM5974_DEV_SPI(hid, i) { \
@@ -416,60 +421,60 @@ static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
static const struct hid_device_id bcm5974_devs[] = {
/* MacbookAir1.1 */
- BCM5974_DEV(APPLE, WELLSPRING_ANSI, BCM5974_FLAG_WELLSPRING1),
- BCM5974_DEV(APPLE, WELLSPRING_ISO, BCM5974_FLAG_WELLSPRING1),
- BCM5974_DEV(APPLE, WELLSPRING_JIS, BCM5974_FLAG_WELLSPRING1),
+ BCM5974_DEV_USB(APPLE, WELLSPRING_ANSI, BCM5974_FLAG_WELLSPRING1),
+ BCM5974_DEV_USB(APPLE, WELLSPRING_ISO, BCM5974_FLAG_WELLSPRING1),
+ BCM5974_DEV_USB(APPLE, WELLSPRING_JIS, BCM5974_FLAG_WELLSPRING1),
/* MacbookProPenryn, aka wellspring2 */
- BCM5974_DEV(APPLE, WELLSPRING2_ANSI, BCM5974_FLAG_WELLSPRING2),
- BCM5974_DEV(APPLE, WELLSPRING2_ISO, BCM5974_FLAG_WELLSPRING2),
- BCM5974_DEV(APPLE, WELLSPRING2_JIS, BCM5974_FLAG_WELLSPRING2),
+ BCM5974_DEV_USB(APPLE, WELLSPRING2_ANSI, BCM5974_FLAG_WELLSPRING2),
+ BCM5974_DEV_USB(APPLE, WELLSPRING2_ISO, BCM5974_FLAG_WELLSPRING2),
+ BCM5974_DEV_USB(APPLE, WELLSPRING2_JIS, BCM5974_FLAG_WELLSPRING2),
/* Macbook5,1 (unibody), aka wellspring3 */
- BCM5974_DEV(APPLE, WELLSPRING3_ANSI, BCM5974_FLAG_WELLSPRING3),
- BCM5974_DEV(APPLE, WELLSPRING3_ISO, BCM5974_FLAG_WELLSPRING3),
- BCM5974_DEV(APPLE, WELLSPRING3_JIS, BCM5974_FLAG_WELLSPRING3),
+ BCM5974_DEV_USB(APPLE, WELLSPRING3_ANSI, BCM5974_FLAG_WELLSPRING3),
+ BCM5974_DEV_USB(APPLE, WELLSPRING3_ISO, BCM5974_FLAG_WELLSPRING3),
+ BCM5974_DEV_USB(APPLE, WELLSPRING3_JIS, BCM5974_FLAG_WELLSPRING3),
/* MacbookAir3,2 (unibody), aka wellspring4 */
- BCM5974_DEV(APPLE, WELLSPRING4_ANSI, BCM5974_FLAG_WELLSPRING4),
- BCM5974_DEV(APPLE, WELLSPRING4_ISO, BCM5974_FLAG_WELLSPRING4),
- BCM5974_DEV(APPLE, WELLSPRING4_JIS, BCM5974_FLAG_WELLSPRING4),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4_ANSI, BCM5974_FLAG_WELLSPRING4),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4_ISO, BCM5974_FLAG_WELLSPRING4),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4_JIS, BCM5974_FLAG_WELLSPRING4),
/* MacbookAir3,1 (unibody), aka wellspring4 */
- BCM5974_DEV(APPLE, WELLSPRING4A_ANSI, BCM5974_FLAG_WELLSPRING4A),
- BCM5974_DEV(APPLE, WELLSPRING4A_ISO, BCM5974_FLAG_WELLSPRING4A),
- BCM5974_DEV(APPLE, WELLSPRING4A_JIS, BCM5974_FLAG_WELLSPRING4A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4A_ANSI, BCM5974_FLAG_WELLSPRING4A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4A_ISO, BCM5974_FLAG_WELLSPRING4A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING4A_JIS, BCM5974_FLAG_WELLSPRING4A),
/* Macbook8 (unibody, March 2011) */
- BCM5974_DEV(APPLE, WELLSPRING5_ANSI, BCM5974_FLAG_WELLSPRING5),
- BCM5974_DEV(APPLE, WELLSPRING5_ISO, BCM5974_FLAG_WELLSPRING5),
- BCM5974_DEV(APPLE, WELLSPRING5_JIS, BCM5974_FLAG_WELLSPRING5),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5_ANSI, BCM5974_FLAG_WELLSPRING5),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5_ISO, BCM5974_FLAG_WELLSPRING5),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5_JIS, BCM5974_FLAG_WELLSPRING5),
/* MacbookAir4,1 (unibody, July 2011) */
- BCM5974_DEV(APPLE, WELLSPRING6A_ANSI, BCM5974_FLAG_WELLSPRING6A),
- BCM5974_DEV(APPLE, WELLSPRING6A_ISO, BCM5974_FLAG_WELLSPRING6A),
- BCM5974_DEV(APPLE, WELLSPRING6A_JIS, BCM5974_FLAG_WELLSPRING6A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6A_ANSI, BCM5974_FLAG_WELLSPRING6A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6A_ISO, BCM5974_FLAG_WELLSPRING6A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6A_JIS, BCM5974_FLAG_WELLSPRING6A),
/* MacbookAir4,2 (unibody, July 2011) */
- BCM5974_DEV(APPLE, WELLSPRING6_ANSI, BCM5974_FLAG_WELLSPRING6),
- BCM5974_DEV(APPLE, WELLSPRING6_ISO, BCM5974_FLAG_WELLSPRING6),
- BCM5974_DEV(APPLE, WELLSPRING6_JIS, BCM5974_FLAG_WELLSPRING6),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6_ANSI, BCM5974_FLAG_WELLSPRING6),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6_ISO, BCM5974_FLAG_WELLSPRING6),
+ BCM5974_DEV_USB(APPLE, WELLSPRING6_JIS, BCM5974_FLAG_WELLSPRING6),
/* Macbook8,2 (unibody) */
- BCM5974_DEV(APPLE, WELLSPRING5A_ANSI, BCM5974_FLAG_WELLSPRING5A),
- BCM5974_DEV(APPLE, WELLSPRING5A_ISO, BCM5974_FLAG_WELLSPRING5A),
- BCM5974_DEV(APPLE, WELLSPRING5A_JIS, BCM5974_FLAG_WELLSPRING5A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5A_ANSI, BCM5974_FLAG_WELLSPRING5A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5A_ISO, BCM5974_FLAG_WELLSPRING5A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING5A_JIS, BCM5974_FLAG_WELLSPRING5A),
/* MacbookPro10,1 (unibody, June 2012) */
/* MacbookPro11,1-3 (unibody, June 2013) */
- BCM5974_DEV(APPLE, WELLSPRING7_ANSI, BCM5974_FLAG_WELLSPRING7),
- BCM5974_DEV(APPLE, WELLSPRING7_ISO, BCM5974_FLAG_WELLSPRING7),
- BCM5974_DEV(APPLE, WELLSPRING7_JIS, BCM5974_FLAG_WELLSPRING7),
+ BCM5974_DEV_USB(APPLE, WELLSPRING7_ANSI, BCM5974_FLAG_WELLSPRING7),
+ BCM5974_DEV_USB(APPLE, WELLSPRING7_ISO, BCM5974_FLAG_WELLSPRING7),
+ BCM5974_DEV_USB(APPLE, WELLSPRING7_JIS, BCM5974_FLAG_WELLSPRING7),
- /* MacbookPro10,2 (unibody, October 2012) */
- BCM5974_DEV(APPLE, WELLSPRING7A_ANSI, BCM5974_FLAG_WELLSPRING7A),
- BCM5974_DEV(APPLE, WELLSPRING7A_ISO, BCM5974_FLAG_WELLSPRING7A),
- BCM5974_DEV(APPLE, WELLSPRING7A_JIS, BCM5974_FLAG_WELLSPRING7A),
+ /* MacbookPro10,2 (unibody, October 2012) */
+ BCM5974_DEV_USB(APPLE, WELLSPRING7A_ANSI, BCM5974_FLAG_WELLSPRING7A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING7A_ISO, BCM5974_FLAG_WELLSPRING7A),
+ BCM5974_DEV_USB(APPLE, WELLSPRING7A_JIS, BCM5974_FLAG_WELLSPRING7A),
/* MacbookAir6,2 (unibody, June 2013) */
BCM5974_DEV(APPLE, WELLSPRING8_ANSI, BCM5974_FLAG_WELLSPRING8),
@@ -748,7 +753,8 @@ bcm5974_attach(device_t dev)
BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
/* finger pressure */
- BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
+ if ((sc->sc_params->tp->caps & SUPPORTS_FORCETOUCH) != 0)
+ BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
/* finger touch area */
BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
diff --git a/sys/dev/hid/hid.h b/sys/dev/hid/hid.h
index 02709d549a56..f164e48341ed 100644
--- a/sys/dev/hid/hid.h
+++ b/sys/dev/hid/hid.h
@@ -57,10 +57,14 @@
#define HUP_SCALE 0x008c
#define HUP_CAMERA_CONTROL 0x0090
#define HUP_ARCADE 0x0091
-#define HUP_APPLE 0x00ff
+#define HUP_RESERVED_FF 0x00ff
#define HUP_FIDO 0xf1d0
-#define HUP_MICROSOFT 0xff00
-#define HUP_HP 0xff01
+#define HUP_VENDOR_00 0xff00
+#define HUP_VENDOR_01 0xff01
+/* XXX compat */
+#define HUP_APPLE HUP_RESERVED_FF
+#define HUP_MICROSOFT HUP_VENDOR_00
+#define HUP_HP HUP_VENDOR_01
/* Usages, generic desktop */
#define HUG_POINTER 0x0001
diff --git a/sys/dev/hwpmc/hwpmc_ibs.c b/sys/dev/hwpmc/hwpmc_ibs.c
index bfc135f06884..a230288f157e 100644
--- a/sys/dev/hwpmc/hwpmc_ibs.c
+++ b/sys/dev/hwpmc/hwpmc_ibs.c
@@ -270,7 +270,7 @@ ibs_stop_pmc(int cpu __diagused, int ri, struct pmc *pm)
* Turn off the ENABLE bit, but unfortunately there are a few quirks
* that generate excess NMIs. Workaround #420 in the Revision Guide
* for AMD Family 10h Processors 41322 Rev. 3.92 March 2012. requires
- * that we clear the count before clearing enable.
+ * that we clear the max count before clearing enable.
*
* Even after clearing the counter spurious NMIs are still possible so
* we use a per-CPU atomic variable to notify the interrupt handler we
@@ -290,7 +290,7 @@ ibs_stop_pmc(int cpu __diagused, int ri, struct pmc *pm)
wrmsr(IBS_FETCH_CTL, config);
break;
case IBS_PMC_OP:
- wrmsr(IBS_FETCH_CTL, config & ~IBS_FETCH_CTL_MAXCNTMASK);
+ wrmsr(IBS_OP_CTL, config & ~IBS_OP_CTL_MAXCNTMASK);
DELAY(1);
config &= ~IBS_OP_CTL_ENABLE;
wrmsr(IBS_OP_CTL, config);
@@ -342,6 +342,8 @@ pmc_ibs_process_fetch(struct pmc *pm, struct trapframe *tf, uint64_t config)
}
pmc_process_interrupt_mp(PMC_HR, pm, tf, &mpd);
+
+ wrmsr(IBS_FETCH_CTL, pm->pm_md.pm_ibs.ibs_ctl | IBS_FETCH_CTL_ENABLE);
}
static void
diff --git a/sys/dev/hwpmc/hwpmc_ibs.h b/sys/dev/hwpmc/hwpmc_ibs.h
index c66d54672543..1616a746ffef 100644
--- a/sys/dev/hwpmc/hwpmc_ibs.h
+++ b/sys/dev/hwpmc/hwpmc_ibs.h
@@ -112,7 +112,7 @@
#define IBS_OP_CTL_VALID (1ULL << 18) /* Valid */
#define IBS_OP_CTL_ENABLE (1ULL << 17) /* Enable */
#define IBS_OP_CTL_L3MISSONLY (1ULL << 16) /* L3 Miss Filtering */
-#define IBS_OP_CTL_MAXCNTMASK 0x0000FFFFULL
+#define IBS_OP_CTL_MAXCNTMASK 0x07F0FFFFULL
#define IBS_OP_CTL_LDLAT_TO_CTL(_c) ((((ldlat) >> 7) - 1) << 59)
#define IBS_OP_INTERVAL_TO_CTL(_c) ((((_c) >> 4) & 0x0000FFFFULL) | ((_c) & 0x07F00000))
diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c
index 6133b52b516f..9533cb81b4a1 100644
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -198,7 +198,6 @@ static int pmc_debugflags_sysctl_handler(SYSCTL_HANDLER_ARGS);
static int pmc_debugflags_parse(char *newstr, char *fence);
#endif
-static bool pmc_is_multipart(struct pmc_sample *ps);
static void pmc_multipart_add(struct pmc_sample *ps, int type,
int length);
static void pmc_multipart_copydata(struct pmc_sample *ps,
@@ -4636,12 +4635,6 @@ pmc_post_callchain_callback(void)
return;
}
-static bool
-pmc_is_multipart(struct pmc_sample *ps)
-{
- return ((ps->ps_flags & PMC_CC_F_MULTIPART) != 0);
-}
-
static void
pmc_multipart_add(struct pmc_sample *ps, int type, int length)
{
diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c
index efb8569a23c0..c3fcb2dbdaed 100644
--- a/sys/dev/iicbus/iic.c
+++ b/sys/dev/iicbus/iic.c
@@ -31,11 +31,13 @@
#include <sys/abi_compat.h>
#include <sys/bus.h>
#include <sys/conf.h>
+#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/proc.h>
#include <sys/sx.h>
#include <sys/systm.h>
#include <sys/uio.h>
@@ -96,7 +98,10 @@ static void iic_identify(driver_t *driver, device_t parent);
static void iicdtor(void *data);
static int iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last);
static int iicuio(struct cdev *dev, struct uio *uio, int ioflag);
-static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags, bool compat32);
+static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d,
+ int flags, bool compat32, bool kernel_msgs);
+static int iic_linux_rdwr(struct file *fp, struct iic_rdwr_data *d,
+ int flags, struct thread *td);
static device_method_t iic_methods[] = {
/* device interface */
@@ -163,6 +168,7 @@ iic_attach(device_t dev)
return (ENXIO);
}
sc->sc_devnode->si_drv1 = sc;
+ sc->sc_devnode->si_drv2 = (void *)iic_linux_rdwr;
return (0);
}
@@ -341,7 +347,7 @@ iic_copyinmsgs32(struct iic_rdwr_data *d, struct iic_msg *buf)
static int
iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags,
- bool compat32 __unused)
+ bool compat32 __unused, bool kernel_msgs)
{
#ifdef COMPAT_FREEBSD32
struct iic_rdwr_data dswab;
@@ -375,7 +381,11 @@ iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags,
error = iic_copyinmsgs32(d, buf);
else
#endif
- error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs);
+ if (kernel_msgs)
+ memcpy(buf, d->msgs, sizeof(*d->msgs) * d->nmsgs);
+ else
+ error = copyin(d->msgs, buf,
+ sizeof(*d->msgs) * d->nmsgs);
if (error != 0) {
free(buf, M_IIC);
return (error);
@@ -425,6 +435,27 @@ iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags,
}
static int
+iic_linux_rdwr(struct file *fp, struct iic_rdwr_data *d, int flags,
+ struct thread *td)
+{
+ struct file *saved_fp;
+ struct iic_cdevpriv *priv;
+ int error;
+
+ saved_fp = td->td_fpop;
+ td->td_fpop = fp;
+ error = devfs_get_cdevpriv((void **)&priv);
+ td->td_fpop = saved_fp;
+ if (error != 0)
+ return (error);
+
+ IIC_LOCK(priv);
+ error = iicrdwr(priv, d, flags, false, true);
+ IIC_UNLOCK(priv);
+ return (error);
+}
+
+static int
iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
{
#ifdef COMPAT_FREEBSD32
@@ -582,7 +613,7 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
compat32 = false;
#endif
error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags,
- compat32);
+ compat32, false);
break;
diff --git a/sys/dev/iicbus/iic.h b/sys/dev/iicbus/iic.h
index f6e9360186e0..badfb76e92eb 100644
--- a/sys/dev/iicbus/iic.h
+++ b/sys/dev/iicbus/iic.h
@@ -31,6 +31,14 @@
#include <sys/ioccom.h>
+#ifdef _KERNEL
+struct file;
+struct iic_rdwr_data;
+struct thread;
+typedef int iic_linux_rdwr_t(struct file *fp, struct iic_rdwr_data *d,
+ int flags, struct thread *td);
+#endif
+
/* Designed to be compatible with linux's struct i2c_msg */
struct iic_msg
{
diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index 88cbeb418ec6..b9d88fcab523 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -36,10 +36,14 @@
#include "opt_rss.h"
#include "ixgbe.h"
+#include "mdio_if.h"
#include "ixgbe_sriov.h"
#include "ifdi_if.h"
+#include "if_ix_mdio_hw.h"
+#include "if_ix_mdio.h"
#include <net/netmap.h>
+#include <dev/mdio/mdio.h>
#include <dev/netmap/netmap_kern.h>
/************************************************************************
@@ -298,6 +302,10 @@ static device_method_t ix_methods[] = {
DEVMETHOD(pci_iov_uninit, iflib_device_iov_uninit),
DEVMETHOD(pci_iov_add_vf, iflib_device_iov_add_vf),
#endif /* PCI_IOV */
+ DEVMETHOD(bus_add_child, device_add_child_ordered),
+ DEVMETHOD(mdio_readreg, ixgbe_mdio_readreg_c22),
+ DEVMETHOD(mdio_writereg, ixgbe_mdio_writereg_c22),
+
DEVMETHOD_END
};
@@ -305,11 +313,13 @@ static driver_t ix_driver = {
"ix", ix_methods, sizeof(struct ixgbe_softc),
};
-DRIVER_MODULE(ix, pci, ix_driver, 0, 0);
+DRIVER_MODULE(mdio, ix, mdio_driver, 0, 0); /* needs to happen before ix */
+DRIVER_MODULE_ORDERED(ix, pci, ix_driver, NULL, NULL, SI_ORDER_ANY); /* needs to be last */
IFLIB_PNP_INFO(pci, ix_driver, ixgbe_vendor_info_array);
MODULE_DEPEND(ix, pci, 1, 1, 1);
MODULE_DEPEND(ix, ether, 1, 1, 1);
MODULE_DEPEND(ix, iflib, 1, 1, 1);
+MODULE_DEPEND(ix, mdio, 1, 1, 1);
static device_method_t ixgbe_if_methods[] = {
DEVMETHOD(ifdi_attach_pre, ixgbe_if_attach_pre),
@@ -709,7 +719,7 @@ ixgbe_initialize_rss_mapping(struct ixgbe_softc *sc)
RSS_HASHTYPE_RSS_TCP_IPV6_EX;
}
- mrqc = IXGBE_MRQC_RSSEN;
+ mrqc = ixgbe_get_mrqc(sc->iov_mode);
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4)
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4;
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4)
@@ -728,7 +738,7 @@ ixgbe_initialize_rss_mapping(struct ixgbe_softc *sc)
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP;
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6_EX)
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP;
- mrqc |= ixgbe_get_mrqc(sc->iov_mode);
+
IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
} /* ixgbe_initialize_rss_mapping */
@@ -1122,10 +1132,13 @@ ixgbe_if_attach_pre(if_ctx_t ctx)
break;
}
- /* Check the FW API version */
- if (hw->mac.type == ixgbe_mac_E610 && ixgbe_check_fw_api_version(sc)) {
- error = EIO;
- goto err_pci;
+ /* Check the FW API version and enable FW logging support for E610 */
+ if (hw->mac.type == ixgbe_mac_E610) {
+ if (ixgbe_check_fw_api_version(sc)) {
+ error = EIO;
+ goto err_pci;
+ }
+ ixgbe_fwlog_set_support_ena(hw);
}
/* Most of the iflib initialization... */
@@ -1271,6 +1284,9 @@ ixgbe_if_attach_post(if_ctx_t ctx)
/* Add sysctls */
ixgbe_add_device_sysctls(ctx);
+ /* Add MDIO bus if required / supported */
+ ixgbe_mdio_attach(sc);
+
/* Init recovery mode timer and state variable */
if (sc->feat_en & IXGBE_FEATURE_RECOVERY_MODE) {
sc->recovery_mode = 0;
@@ -3399,6 +3415,9 @@ ixgbe_add_debug_sysctls(struct ixgbe_softc *sc)
if (sc->feat_en & IXGBE_FEATURE_DBG_DUMP)
ixgbe_add_debug_dump_sysctls(sc);
+
+ if (sc->feat_en & IXGBE_FEATURE_FW_LOGGING)
+ ixgbe_add_fw_logging_tunables(sc, sc->debug_sysctls);
} /* ixgbe_add_debug_sysctls */
/************************************************************************
@@ -4495,6 +4514,10 @@ ixgbe_handle_fw_event(void *context)
sc->task_requests |= IXGBE_REQUEST_TASK_LSC;
break;
+ case ixgbe_aci_opc_fw_logs_event:
+ ixgbe_fwlog_event_dump(&sc->hw, &event.desc, event.msg_buf);
+ break;
+
case ixgbe_aci_opc_temp_tca_event:
if (hw->adapter_stopped == FALSE)
ixgbe_if_stop(ctx);
@@ -5731,6 +5754,7 @@ ixgbe_init_device_features(struct ixgbe_softc *sc)
case ixgbe_mac_E610:
sc->feat_cap |= IXGBE_FEATURE_RECOVERY_MODE;
sc->feat_cap |= IXGBE_FEATURE_DBG_DUMP;
+ sc->feat_cap |= IXGBE_FEATURE_FW_LOGGING;
error = ixgbe_get_caps(&sc->hw);
if (error == 0 && sc->hw.func_caps.common_cap.eee_support != 0)
sc->feat_cap |= IXGBE_FEATURE_EEE;
@@ -5758,6 +5782,9 @@ ixgbe_init_device_features(struct ixgbe_softc *sc)
/* FW Debug Dump */
if (sc->feat_cap & IXGBE_FEATURE_DBG_DUMP)
sc->feat_en |= IXGBE_FEATURE_DBG_DUMP;
+ /* FW Logging */
+ if (sc->feat_cap & IXGBE_FEATURE_FW_LOGGING)
+ sc->feat_en |= IXGBE_FEATURE_FW_LOGGING;
/* Enabled via global sysctl... */
/* Flow Director */
diff --git a/sys/dev/ixgbe/if_ix_mdio.c b/sys/dev/ixgbe/if_ix_mdio.c
new file mode 100644
index 000000000000..3aa7ea80c3a7
--- /dev/null
+++ b/sys/dev/ixgbe/if_ix_mdio.c
@@ -0,0 +1,158 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_rss.h"
+
+#include "ixgbe.h"
+#include "mdio_if.h"
+#include "ixgbe_sriov.h"
+#include "ifdi_if.h"
+#include "if_ix_mdio_hw.h"
+#include "if_ix_mdio.h"
+
+#include <dev/mdio/mdio.h>
+
+/**
+ * @brief Return if the given ixgbe chipset supports clause 22 MDIO bus access.
+ *
+ * Although technically all of the ixgbe chipsets support an MDIO
+ * bus interface, there's a bunch of factors controlling whether
+ * this should be exposed for external control.
+ *
+ * This functionr returns true if it supports an MDIO bus and
+ * clause 22 transactions.
+ */
+static bool
+ixgbe_has_mdio_bus_clause22(struct ixgbe_hw *hw)
+{
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_X550EM_A_KR:
+ case IXGBE_DEV_ID_X550EM_A_KR_L:
+ case IXGBE_DEV_ID_X550EM_A_SFP_N:
+ case IXGBE_DEV_ID_X550EM_A_SGMII:
+ case IXGBE_DEV_ID_X550EM_A_SGMII_L:
+ case IXGBE_DEV_ID_X550EM_A_10G_T:
+ case IXGBE_DEV_ID_X550EM_A_SFP:
+ case IXGBE_DEV_ID_X550EM_A_1G_T:
+ case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+ return (true);
+ }
+ return (false);
+}
+
+
+
+/**
+ * @brief Initiate a clause-22 MDIO read transfer.
+ *
+ * Note this is only officially supported for a small subset
+ * of NICs, notably the X552/X553 devices. This must not be
+ * called for other chipsets.
+ */
+int
+ixgbe_mdio_readreg_c22(device_t dev, int phy, int reg)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+ struct sx *iflib_ctx_lock = iflib_ctx_lock_get(ctx);
+ struct ixgbe_softc *sc = iflib_get_softc(ctx);
+ struct ixgbe_hw *hw = &sc->hw;
+ uint16_t val = 0;
+ int32_t ret = 0;
+
+ if (! ixgbe_has_mdio_bus_clause22(hw))
+ return (-1);
+
+ sx_xlock(iflib_ctx_lock);
+ ret = ixgbe_read_mdio_c22(hw, phy, reg, &val);
+ if (ret != IXGBE_SUCCESS) {
+ device_printf(dev, "%s: read_mdi_22 failed (%d)\n",
+ __func__, ret);
+ sx_xunlock(iflib_ctx_lock);
+ return (-1);
+ }
+ sx_xunlock(iflib_ctx_lock);
+ return (val);
+}
+
+/**
+ * @brief Initiate a clause-22 MDIO write transfer.
+ *
+ * Note this is only officially supported for a small subset
+ * of NICs, notably the X552/X553 devices. This must not be
+ * called for other chipsets.
+ */
+int
+ixgbe_mdio_writereg_c22(device_t dev, int phy, int reg, int data)
+{
+ if_ctx_t ctx = device_get_softc(dev);
+ struct sx *iflib_ctx_lock = iflib_ctx_lock_get(ctx);
+ struct ixgbe_softc *sc = iflib_get_softc(ctx);
+ struct ixgbe_hw *hw = &sc->hw;
+ int32_t ret;
+
+ if (! ixgbe_has_mdio_bus_clause22(hw))
+ return (-1);
+
+ sx_xlock(iflib_ctx_lock);
+ ret = ixgbe_write_mdio_c22(hw, phy, reg, data);
+ if (ret != IXGBE_SUCCESS) {
+ device_printf(dev, "%s: write_mdi_22 failed (%d)\n",
+ __func__, ret);
+ sx_xunlock(iflib_ctx_lock);
+ return (-1);
+ }
+ sx_xunlock(iflib_ctx_lock);
+ return (0);
+}
+
+/**
+ * @brief Attach the MDIO bus if one exists.
+ */
+void
+ixgbe_mdio_attach(struct ixgbe_softc *sc)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ int enable_mdio = 0;
+
+ /*
+ * This explicitly needs to be enabled regardless of whether
+ * the device / instance supports an external MDIO bus.
+ */
+ if (resource_int_value(device_get_name(sc->dev),
+ device_get_unit(sc->dev), "enable_mdio", &enable_mdio) == 0) {
+ if (enable_mdio == 0)
+ return;
+ } else
+ return;
+
+ if (! ixgbe_has_mdio_bus_clause22(hw))
+ return;
+
+ device_add_child(sc->dev, "mdio", DEVICE_UNIT_ANY);
+ bus_attach_children(sc->dev);
+}
diff --git a/sys/dev/ixgbe/if_ix_mdio.h b/sys/dev/ixgbe/if_ix_mdio.h
new file mode 100644
index 000000000000..f9fe99275b2b
--- /dev/null
+++ b/sys/dev/ixgbe/if_ix_mdio.h
@@ -0,0 +1,34 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IF_IX_MDIO_H_
+#define _IF_IX_MDIO_H_
+
+int ixgbe_mdio_readreg_c22(device_t, int, int);
+int ixgbe_mdio_writereg_c22(device_t, int, int, int);
+void ixgbe_mdio_attach(struct ixgbe_softc *);
+
+#endif /* _IF_IX_MDIO_H */
diff --git a/sys/dev/ixgbe/if_ix_mdio_hw.c b/sys/dev/ixgbe/if_ix_mdio_hw.c
new file mode 100644
index 000000000000..581ed09f27e3
--- /dev/null
+++ b/sys/dev/ixgbe/if_ix_mdio_hw.c
@@ -0,0 +1,181 @@
+/******************************************************************************
+ SPDX-License-Identifier: BSD-3-Clause
+
+ Copyright (c) 2001-2024, Intel Corporation
+ All rights reserved.
+ Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+#include "ixgbe_phy.h"
+
+#include "if_ix_mdio_hw.h"
+
+/*
+ * These routines are separate from the rest of ixgbe for now to make merging
+ * easier.
+ */
+
+static s32
+ixgbe_read_mdio_unlocked_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 *phy_data)
+{
+ u32 i, data, command;
+
+ /* Setup and write the read command */
+ command = (reg << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (phy << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ IXGBE_MSCA_OLD_PROTOCOL | IXGBE_MSCA_READ_AUTOINC |
+ IXGBE_MSCA_MDI_COMMAND;
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /* Check every 10 usec to see if the access completed.
+ * The MDI Command bit will clear when the operation is
+ * complete
+ */
+ for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
+ usec_delay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+ if (!(command & IXGBE_MSCA_MDI_COMMAND))
+ break;
+ }
+
+ if (command & IXGBE_MSCA_MDI_COMMAND)
+ return IXGBE_ERR_PHY;
+
+ /* Read operation is complete. Get the data from MSRWD */
+ data = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+ data >>= IXGBE_MSRWD_READ_DATA_SHIFT;
+ *phy_data = (u16)data;
+
+ return IXGBE_SUCCESS;
+}
+
+static s32
+ixgbe_write_mdio_unlocked_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 phy_data)
+{
+ u32 i, command;
+
+ /* Put the data in the MDI single read and write data register*/
+ IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)phy_data);
+
+ /* Setup and write the write command */
+ command = (reg << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (phy << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ IXGBE_MSCA_OLD_PROTOCOL | IXGBE_MSCA_WRITE |
+ IXGBE_MSCA_MDI_COMMAND;
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /* Check every 10 usec to see if the access completed.
+ * The MDI Command bit will clear when the operation is
+ * complete
+ */
+ for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
+ usec_delay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+ if (!(command & IXGBE_MSCA_MDI_COMMAND))
+ break;
+ }
+
+ if (command & IXGBE_MSCA_MDI_COMMAND)
+ return IXGBE_ERR_PHY;
+
+ return IXGBE_SUCCESS;
+}
+
+/*
+ * Return true if the MAC is an X55x backplane.
+ *
+ * These have a single MDIO PHY semaphore (PHY0) and also require the
+ * token semaphore.
+ */
+static bool
+ixgbe_check_mdio_is_x550em(struct ixgbe_hw *hw)
+{
+
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_X550EM_A_KR:
+ case IXGBE_DEV_ID_X550EM_A_KR_L:
+ case IXGBE_DEV_ID_X550EM_A_SFP_N:
+ case IXGBE_DEV_ID_X550EM_A_SGMII:
+ case IXGBE_DEV_ID_X550EM_A_SGMII_L:
+ case IXGBE_DEV_ID_X550EM_A_10G_T:
+ case IXGBE_DEV_ID_X550EM_A_SFP:
+ case IXGBE_DEV_ID_X550EM_A_1G_T:
+ case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+ return true;
+ default:
+ return false;
+ }
+}
+
+s32
+ixgbe_read_mdio_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 *phy_data)
+{
+ u32 gssr = hw->phy.phy_semaphore_mask;
+ s32 ret;
+
+ if (ixgbe_check_mdio_is_x550em(hw))
+ gssr |= IXGBE_GSSR_PHY0_SM | IXGBE_GSSR_TOKEN_SM;
+
+ if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) {
+ *phy_data = -1;
+ return IXGBE_ERR_TIMEOUT;
+ }
+
+ ret = ixgbe_read_mdio_unlocked_c22(hw, phy, reg, phy_data);
+ if (ret != IXGBE_SUCCESS)
+ *phy_data = -1;
+
+ hw->mac.ops.release_swfw_sync(hw, gssr);
+ return ret;
+}
+
+s32
+ixgbe_write_mdio_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 data)
+{
+ u32 gssr = hw->phy.phy_semaphore_mask;
+ s32 ret;
+
+ if (ixgbe_check_mdio_is_x550em(hw))
+ gssr |= IXGBE_GSSR_PHY0_SM | IXGBE_GSSR_TOKEN_SM;
+
+ if (hw->mac.ops.acquire_swfw_sync(hw, gssr))
+ return IXGBE_ERR_TIMEOUT;
+
+ ret = ixgbe_write_mdio_unlocked_c22(hw, phy, reg, data);
+
+ hw->mac.ops.release_swfw_sync(hw, gssr);
+ return ret;
+}
diff --git a/sys/dev/ixgbe/if_ix_mdio_hw.h b/sys/dev/ixgbe/if_ix_mdio_hw.h
new file mode 100644
index 000000000000..b2db5d431819
--- /dev/null
+++ b/sys/dev/ixgbe/if_ix_mdio_hw.h
@@ -0,0 +1,33 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IF_IX_MDIO_HW_H_
+#define _IF_IX_MDIO_HW_H_
+
+s32 ixgbe_read_mdio_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 *phy_data);
+s32 ixgbe_write_mdio_c22(struct ixgbe_hw *hw, u16 phy, u16 reg, u16 data);
+
+#endif /* _IF_IX_MDIO_HW_H_ */
diff --git a/sys/dev/ixgbe/if_sriov.c b/sys/dev/ixgbe/if_sriov.c
index 1998cdb016f7..47f1a1279e2f 100644
--- a/sys/dev/ixgbe/if_sriov.c
+++ b/sys/dev/ixgbe/if_sriov.c
@@ -170,7 +170,7 @@ ixgbe_get_mrqc(int iov_mode)
mrqc = IXGBE_MRQC_VMDQRSS32EN;
break;
case IXGBE_NO_VM:
- mrqc = 0;
+ mrqc = IXGBE_MRQC_RSSEN;
break;
default:
panic("Unexpected SR-IOV mode %d", iov_mode);
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 624b71acabea..9120ca5a37ff 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -607,6 +607,11 @@ int ixgbe_setup_receive_structures(struct ixgbe_softc *);
void ixgbe_free_receive_structures(struct ixgbe_softc *);
int ixgbe_get_regs(SYSCTL_HANDLER_ARGS);
+void ixgbe_add_fw_logging_tunables(struct ixgbe_softc *sc,
+ struct sysctl_oid *parent);
+
+#define IXGBE_STR_BUF_LEN 32
+
#include "ixgbe_bypass.h"
#include "ixgbe_fdir.h"
#include "ixgbe_rss.h"
diff --git a/sys/dev/ixgbe/ixgbe_common.c b/sys/dev/ixgbe/ixgbe_common.c
index bff022585a03..9e827d2e5473 100644
--- a/sys/dev/ixgbe/ixgbe_common.c
+++ b/sys/dev/ixgbe/ixgbe_common.c
@@ -4631,11 +4631,11 @@ s32 ixgbe_hic_unlocked(struct ixgbe_hw *hw, u32 *buffer, u32 length,
/* Setting this bit tells the ARC that a new command is pending. */
IXGBE_WRITE_REG(hw, IXGBE_HICR, hicr | IXGBE_HICR_C);
- for (i = 0; i < timeout; i++) {
+ for (i = 0; i < timeout * 1000; i++) {
hicr = IXGBE_READ_REG(hw, IXGBE_HICR);
if (!(hicr & IXGBE_HICR_C))
break;
- msec_delay(1);
+ usec_delay(1);
}
/* For each command except "Apply Update" perform
diff --git a/sys/dev/ixgbe/ixgbe_e610.c b/sys/dev/ixgbe/ixgbe_e610.c
index b76d96814933..21066f95a16e 100644
--- a/sys/dev/ixgbe/ixgbe_e610.c
+++ b/sys/dev/ixgbe/ixgbe_e610.c
@@ -3830,6 +3830,492 @@ s32 ixgbe_handle_nvm_access(struct ixgbe_hw *hw,
}
/**
+ * ixgbe_fwlog_cache_cfg - Cache FW logging config
+ * @hw: pointer to the HW structure
+ * @cfg: config to cache
+ *
+ * Cache FW logging config.
+ */
+static void ixgbe_fwlog_cache_cfg(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_cfg *cfg)
+{
+ hw->fwlog_cfg = *cfg;
+}
+
+/**
+ * ixgbe_fwlog_valid_module_entries - validate all the module entry IDs and
+ * log levels
+ * @hw: pointer to the HW structure
+ * @entries: entries to validate
+ * @num_entries: number of entries to validate
+ *
+ * Checks if all the module entry IDs and log levels are valid.
+ *
+ * Return: true if all the module entry IDs and log levels are valid,
+ * otherwise false.
+ */
+static bool ixgbe_fwlog_valid_module_entries(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ UNREFERENCED_1PARAMETER(hw);
+
+ if (!entries) {
+ return false;
+ }
+
+ if (!num_entries) {
+ return false;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ struct ixgbe_fwlog_module_entry *entry = &entries[i];
+
+ if (entry->module_id >= IXGBE_ACI_FW_LOG_ID_MAX) {
+ return false;
+ }
+
+ if (entry->log_level >= IXGBE_FWLOG_LEVEL_INVALID) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * ixgbe_fwlog_valid_cfg - validate configuration
+ * @hw: pointer to the HW structure
+ * @cfg: config to validate
+ *
+ * Validate the entire configuration.
+ *
+ * Return: true if the entire configuration is valid, otherwise false.
+ */
+static bool ixgbe_fwlog_valid_cfg(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_cfg *cfg)
+{
+ if (!cfg) {
+ return false;
+ }
+
+ if (cfg->log_resolution < IXGBE_ACI_FW_LOG_MIN_RESOLUTION ||
+ cfg->log_resolution > IXGBE_ACI_FW_LOG_MAX_RESOLUTION) {
+ return false;
+ }
+
+ if (!ixgbe_fwlog_valid_module_entries(hw, cfg->module_entries,
+ IXGBE_ACI_FW_LOG_ID_MAX))
+ return false;
+
+ return true;
+}
+
+/**
+ * ixgbe_fwlog_init - Initialize cached structures for tracking FW logging
+ * @hw: pointer to the HW structure
+ * @cfg: config used to initialize the cached structures
+ *
+ * Initialize cached structures for tracking FW logging
+ * Called on driver initialization and before calling
+ * ixgbe_init_hw(). Firmware logging will be configured based on these settings
+ * and also the PF will be registered on init.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_init(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ if (!ixgbe_fwlog_valid_cfg(hw, cfg))
+ return IXGBE_ERR_PARAM;
+
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_aci_fwlog_set - Set FW logging configuration
+ * @hw: pointer to the HW structure
+ * @entries: entries to configure
+ * @num_entries: number of @entries
+ * @options: options from ixgbe_fwlog_cfg->options structure
+ * @log_resolution: logging resolution
+ *
+ * Set FW logging configuration using ACI command (0xFF30).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_set(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries, u16 options, u16 log_resolution)
+{
+ struct ixgbe_aci_cmd_fw_log_cfg_resp fw_modules[IXGBE_ACI_FW_LOG_ID_MAX];
+ struct ixgbe_aci_cmd_fw_log *cmd;
+ struct ixgbe_aci_desc desc;
+ s32 status;
+ u16 i;
+
+ if (num_entries > IXGBE_ACI_FW_LOG_ID_MAX)
+ return IXGBE_ERR_PARAM;
+
+ for (i = 0; i < num_entries; i++) {
+ fw_modules[i].module_identifier =
+ IXGBE_CPU_TO_LE16(entries[i].module_id);
+ fw_modules[i].log_level = entries[i].log_level;
+ }
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_config);
+ desc.flags |= IXGBE_CPU_TO_LE16(IXGBE_ACI_FLAG_RD);
+
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = IXGBE_ACI_FW_LOG_CONF_SET_VALID;
+ cmd->ops.cfg.log_resolution = IXGBE_CPU_TO_LE16(log_resolution);
+ cmd->ops.cfg.mdl_cnt = IXGBE_CPU_TO_LE16(num_entries);
+
+ if (options & IXGBE_FWLOG_OPTION_ARQ_ENA)
+ cmd->cmd_flags |= IXGBE_ACI_FW_LOG_CONF_AQ_EN;
+ if (options & IXGBE_FWLOG_OPTION_UART_ENA)
+ cmd->cmd_flags |= IXGBE_ACI_FW_LOG_CONF_UART_EN;
+
+ status = ixgbe_aci_send_cmd(hw, &desc, fw_modules,
+ sizeof(*fw_modules) * num_entries);
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_supported - Cached for whether FW supports FW logging or not
+ * @hw: pointer to the HW structure
+ *
+ * This will always return false if called before ixgbe_init_hw(), so it must be
+ * called after ixgbe_init_hw().
+ *
+ * Return: true if FW supports FW logging.
+ * If this function is called before ixgbe_init_hw(), return false.
+ */
+bool ixgbe_fwlog_supported(struct ixgbe_hw *hw)
+{
+ return hw->fwlog_support_ena;
+}
+
+/**
+ * ixgbe_fwlog_set - Set the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config used to set firmware logging
+ *
+ * Call this function whenever the driver needs to set the firmware
+ * logging configuration. It can be called on initialization, reset, or during
+ * runtime.
+ *
+ * If the PF wishes to receive FW logging then it must register via
+ * ixgbe_fwlog_register. Note, that ixgbe_fwlog_register does not need to
+ * be called for init.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_set(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (!ixgbe_fwlog_valid_cfg(hw, cfg))
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_aci_fwlog_set(hw, cfg->module_entries,
+ IXGBE_ACI_FW_LOG_ID_MAX, cfg->options,
+ cfg->log_resolution);
+ if (!status)
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_update_cached_entries - Update module entries in cached
+ * FW logging config
+ * @hw: pointer to the HW structure
+ * @entries: entries to cache
+ * @num_entries: number of @entries
+ *
+ * Update module entries in cached FW logging config.
+ */
+static void ixgbe_fwlog_update_cached_entries(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ for (i = 0; i < num_entries; i++) {
+ struct ixgbe_fwlog_module_entry *updated = &entries[i];
+ u16 j;
+
+ for (j = 0; j < IXGBE_ACI_FW_LOG_ID_MAX; j++) {
+ struct ixgbe_fwlog_module_entry *cached =
+ &hw->fwlog_cfg.module_entries[j];
+
+ if (cached->module_id == updated->module_id) {
+ cached->log_level = updated->log_level;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * ixgbe_fwlog_update_modules - Update the log level 1 or more
+ * FW logging modules
+ * @hw: pointer to the HW structure
+ * @entries: array of ixgbe_fwlog_module_entry(s)
+ * @num_entries: number of entries
+ *
+ * Update the log level of 1 or more FW logging modules via module ID.
+ *
+ * Only the entries passed in will be affected. All other firmware logging
+ * settings will be unaffected.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_update_modules(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ struct ixgbe_fwlog_cfg cfg;
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (num_entries > IXGBE_ACI_FW_LOG_ID_MAX)
+ return IXGBE_ERR_PARAM;
+
+ if (!ixgbe_fwlog_valid_module_entries(hw, entries, num_entries))
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_fwlog_get(hw, &cfg);
+ if (status)
+ goto status_out;
+
+ status = ixgbe_aci_fwlog_set(hw, entries, num_entries, cfg.options,
+ cfg.log_resolution);
+ if (!status)
+ ixgbe_fwlog_update_cached_entries(hw, entries, num_entries);
+
+status_out:
+ return status;
+}
+
+/**
+ * ixgbe_aci_fwlog_register - Register PF for firmware logging events.
+ * @hw: pointer to the HW structure
+ * @reg: true to register and false to unregister
+ *
+ * Register a PF for firmware logging events using ACI command (0xFF31).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_register(struct ixgbe_hw *hw, bool reg)
+{
+ struct ixgbe_aci_desc desc;
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_register);
+
+ if (reg)
+ desc.params.fw_log.cmd_flags = IXGBE_ACI_FW_LOG_AQ_REGISTER;
+
+ return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
+}
+
+/**
+ * ixgbe_fwlog_register - Register the PF for firmware logging
+ * @hw: pointer to the HW structure
+ *
+ * After this call the PF will start to receive firmware logging based on the
+ * configuration set in ixgbe_fwlog_set.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_register(struct ixgbe_hw *hw)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ status = ixgbe_aci_fwlog_register(hw, true);
+
+ if (!status)
+ hw->fwlog_cfg.options |= IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_unregister - Unregister the PF from firmware logging
+ * @hw: pointer to the HW structure
+ *
+ * Make an attempt to unregister the PF from firmware logging.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_unregister(struct ixgbe_hw *hw)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ status = ixgbe_aci_fwlog_register(hw, false);
+ if (!status)
+ hw->fwlog_cfg.options &= ~IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ixgbe_aci_fwlog_get - Get the current firmware logging configuration
+ * @hw: pointer to the HW structure
+ * @cfg: firmware logging configuration to populate
+ *
+ * Make an attempt to get the current firmware logging
+ * configuration using ACI command (0xFF32).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ struct ixgbe_aci_cmd_fw_log_cfg_resp *fw_modules;
+ struct ixgbe_aci_cmd_fw_log *cmd;
+ struct ixgbe_aci_desc desc;
+ u16 i, module_id_cnt;
+ u8 *buf = NULL;
+ s32 status;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_query);
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = IXGBE_ACI_FW_LOG_AQ_QUERY;
+
+ buf = (u8 *)ixgbe_malloc(hw, IXGBE_ACI_MAX_BUFFER_SIZE);
+ if (!buf)
+ return IXGBE_ERR_OUT_OF_MEM;
+
+ status = ixgbe_aci_send_cmd(hw, &desc, buf, IXGBE_ACI_MAX_BUFFER_SIZE);
+ if (status) {
+ goto status_out;
+ }
+
+ module_id_cnt = IXGBE_LE16_TO_CPU(cmd->ops.cfg.mdl_cnt);
+ if (module_id_cnt > IXGBE_ACI_FW_LOG_ID_MAX) {
+ module_id_cnt = IXGBE_ACI_FW_LOG_ID_MAX;
+ }
+
+ cfg->log_resolution = (u8)IXGBE_LE16_TO_CPU(cmd->ops.cfg.log_resolution);
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_CONF_AQ_EN)
+ cfg->options |= IXGBE_FWLOG_OPTION_ARQ_ENA;
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_CONF_UART_EN)
+ cfg->options |= IXGBE_FWLOG_OPTION_UART_ENA;
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_QUERY_REGISTERED)
+ cfg->options |= IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ fw_modules = (struct ixgbe_aci_cmd_fw_log_cfg_resp *)buf;
+
+ for (i = 0; i < module_id_cnt; i++) {
+ struct ixgbe_aci_cmd_fw_log_cfg_resp *fw_module = &fw_modules[i];
+
+ cfg->module_entries[i].module_id =
+ IXGBE_LE16_TO_CPU(fw_module->module_identifier);
+ cfg->module_entries[i].log_level = fw_module->log_level;
+ }
+
+status_out:
+ if (buf)
+ ixgbe_free(hw, buf);
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_set_support_ena - Set if FW logging is supported by FW
+ * @hw: pointer to the HW struct
+ *
+ * If FW returns success to the ixgbe_aci_fwlog_get call then it supports FW
+ * logging, else it doesn't. Set the fwlog_support_ena flag accordingly.
+ *
+ * This function is only meant to be called during driver init to determine if
+ * the FW support FW logging.
+ *
+ * Return: the exit code of the operation.
+ */
+void ixgbe_fwlog_set_support_ena(struct ixgbe_hw *hw)
+{
+ struct ixgbe_fwlog_cfg cfg;
+ s32 status;
+
+ hw->fwlog_support_ena = false;
+
+ /* don't call ixgbe_fwlog_get() because that would overwrite the cached
+ * configuration from the call to ixgbe_fwlog_init(), which is expected
+ * to be called prior to this function
+ */
+ status = ixgbe_aci_fwlog_get(hw, &cfg);
+ if (!status)
+ hw->fwlog_support_ena = true;
+}
+
+/**
+ * ixgbe_fwlog_get - Get the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config to populate based on current firmware logging settings
+ *
+ * Get the current firmware logging settings.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (!cfg)
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_aci_fwlog_get(hw, cfg);
+ if (status)
+ return status;
+
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_fwlog_event_dump - Dump the event received over the Admin Receive Queue
+ * @hw: pointer to the HW structure
+ * @desc: Admin Receive Queue descriptor
+ * @buf: buffer that contains the FW log event data
+ *
+ * If the driver receives the ixgbe_aci_opc_fw_logs_event on the Admin Receive
+ * Queue, then it should call this function to dump the FW log data.
+ */
+void ixgbe_fwlog_event_dump(struct ixgbe_hw *hw,
+ struct ixgbe_aci_desc *desc, void *buf)
+{
+ if (!ixgbe_fwlog_supported(hw))
+ return;
+
+ ixgbe_info_fwlog(hw, 32, 1, (u8 *)buf,
+ IXGBE_LE16_TO_CPU(desc->datalen));
+}
+
+/**
* ixgbe_aci_set_health_status_config - Configure FW health events
* @hw: pointer to the HW struct
* @event_source: type of diagnostic events to enable
diff --git a/sys/dev/ixgbe/ixgbe_e610.h b/sys/dev/ixgbe/ixgbe_e610.h
index 94e600139499..7af5506d85e8 100644
--- a/sys/dev/ixgbe/ixgbe_e610.h
+++ b/sys/dev/ixgbe/ixgbe_e610.h
@@ -169,6 +169,19 @@ s32 ixgbe_handle_nvm_access(struct ixgbe_hw *hw,
s32 ixgbe_aci_set_health_status_config(struct ixgbe_hw *hw, u8 event_source);
+s32 ixgbe_fwlog_init(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+bool ixgbe_fwlog_supported(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_set(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+s32 ixgbe_fwlog_update_modules(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries);
+s32 ixgbe_fwlog_register(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_unregister(struct ixgbe_hw *hw);
+void ixgbe_fwlog_set_support_ena(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+void ixgbe_fwlog_event_dump(struct ixgbe_hw *hw,
+ struct ixgbe_aci_desc *desc, void *buf);
+
/* E610 operations */
s32 ixgbe_init_ops_E610(struct ixgbe_hw *hw);
s32 ixgbe_reset_hw_E610(struct ixgbe_hw *hw);
diff --git a/sys/dev/ixgbe/ixgbe_features.h b/sys/dev/ixgbe/ixgbe_features.h
index bee9040319d8..bbc7507b29ac 100644
--- a/sys/dev/ixgbe/ixgbe_features.h
+++ b/sys/dev/ixgbe/ixgbe_features.h
@@ -58,6 +58,7 @@
#define IXGBE_FEATURE_NEEDS_CTXD (u32)(1 << 13)
#define IXGBE_FEATURE_RECOVERY_MODE (u32)(1 << 15)
#define IXGBE_FEATURE_DBG_DUMP (u32)(1 << 16)
+#define IXGBE_FEATURE_FW_LOGGING (u32)(1 << 17)
/* Check for OS support. Undefine features if not included in the OS */
#ifndef PCI_IOV
diff --git a/sys/dev/ixgbe/ixgbe_fw_logging.c b/sys/dev/ixgbe/ixgbe_fw_logging.c
new file mode 100644
index 000000000000..6202d504423f
--- /dev/null
+++ b/sys/dev/ixgbe/ixgbe_fw_logging.c
@@ -0,0 +1,467 @@
+/**
+ * @file ixgbe_fw_logging.c
+ * @brief firmware logging sysctls
+ *
+ * Contains sysctls to enable and configure firmware logging debug support.
+ */
+
+ #include "ixgbe.h"
+
+ /**
+ * ixgbe_reconfig_fw_log - Re-program firmware logging configuration
+ * @sc: private softc structure
+ * @cfg: firmware log configuration to latch
+ *
+ * If the adminq is currently active, ask firmware to update the logging
+ * configuration. If the adminq is currently down, then do nothing. In this
+ * case, ixgbe_init_hw() will re-configure firmware logging as soon as it brings
+ * up the adminq.
+ */
+ static int
+ ixgbe_reconfig_fw_log(struct ixgbe_softc *sc, struct ixgbe_fwlog_cfg *cfg)
+ {
+ int status;
+
+ ixgbe_fwlog_init(&sc->hw, cfg);
+
+ if (!ixgbe_fwlog_supported(&sc->hw))
+ return (0);
+
+ status = ixgbe_fwlog_set(&sc->hw, cfg);
+ if (status != IXGBE_SUCCESS) {
+ DEBUGOUT1("Failed to reconfigure firmware logging, status %d\n",
+ status);
+ return (ENODEV);
+ }
+
+ return (0);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_set_cfg_options - Sysctl for setting fwlog cfg options
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: option to adjust
+ * @req: sysctl request pointer
+ *
+ * On read: displays whether firmware logging was reported during attachment
+ * On write: enables/disables firmware logging during attach phase
+ *
+ * This has no effect on the legacy (V1) version of firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_set_cfg_options(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int error;
+ u16 option = (u16)arg2;
+ bool enabled;
+
+ enabled = !!(cfg->options & option);
+
+ error = sysctl_handle_bool(oidp, &enabled, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (enabled)
+ cfg->options |= option;
+ else
+ cfg->options &= ~option;
+
+ return ixgbe_reconfig_fw_log(sc, cfg);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_log_resolution - Sysctl for setting log message resolution
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: __unused__
+ * @req: sysctl request pointer
+ *
+ * On read: displays message queue limit before posting
+ * On write: sets message queue limit before posting
+ *
+ * This has no effect on the legacy (V1) version of firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_log_resolution(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int error;
+ u8 resolution;
+
+ UNREFERENCED_PARAMETER(arg2);
+
+ resolution = cfg->log_resolution;
+
+ error = sysctl_handle_8(oidp, &resolution, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if ((resolution < IXGBE_ACI_FW_LOG_MIN_RESOLUTION) ||
+ (resolution > IXGBE_ACI_FW_LOG_MAX_RESOLUTION)) {
+ DEBUGOUT("Log resolution out-of-bounds\n");
+ return (EINVAL);
+ }
+
+ cfg->log_resolution = resolution;
+
+ return ixgbe_reconfig_fw_log(sc, cfg);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_register - Sysctl for (de)registering firmware logs
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: __unused__
+ * @req: sysctl request pointer
+ *
+ * On read: displays whether firmware logging is registered
+ * On write: (de)registers firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_register(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int status;
+ int error;
+ u8 enabled;
+
+ UNREFERENCED_PARAMETER(arg2);
+
+ if (cfg->options & IXGBE_FWLOG_OPTION_IS_REGISTERED)
+ enabled = true;
+ else
+ enabled = false;
+
+ error = sysctl_handle_bool(oidp, &enabled, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (enabled) {
+ status = ixgbe_fwlog_register(&sc->hw);
+ if (status == IXGBE_SUCCESS)
+ sc->feat_en |= IXGBE_FEATURE_FW_LOGGING;
+ } else {
+ status = ixgbe_fwlog_unregister(&sc->hw);
+ if (status == IXGBE_SUCCESS)
+ sc->feat_en &= ~IXGBE_FEATURE_FW_LOGGING;
+ }
+
+ if (status != IXGBE_SUCCESS)
+ return (EIO);
+
+ return (0);
+ }
+
+ /**
+ * ixgbe_log_sev_str - Convert log level to a string
+ * @log_level: the log level to convert
+ *
+ * Convert the u8 log level of a FW logging module into a readable
+ * string for outputting in a sysctl.
+ */
+ struct ixgbe_str_buf {
+ char str[IXGBE_STR_BUF_LEN];
+ };
+
+ static struct ixgbe_str_buf
+ _ixgbe_log_sev_str(u8 log_level)
+ {
+ struct ixgbe_str_buf buf = { .str = "" };
+ const char *str = NULL;
+
+ switch (log_level) {
+ case IXGBE_FWLOG_LEVEL_NONE:
+ str = "none";
+ break;
+ case IXGBE_FWLOG_LEVEL_ERROR:
+ str = "error";
+ break;
+ case IXGBE_FWLOG_LEVEL_WARNING:
+ str = "warning";
+ break;
+ case IXGBE_FWLOG_LEVEL_NORMAL:
+ str = "normal";
+ break;
+ case IXGBE_FWLOG_LEVEL_VERBOSE:
+ str = "verbose";
+ break;
+ default:
+ break;
+ }
+
+ if (str)
+ snprintf(buf.str, IXGBE_STR_BUF_LEN, "%s", str);
+ else
+ snprintf(buf.str, IXGBE_STR_BUF_LEN, "%u", log_level);
+
+ return buf;
+ }
+
+ #define ixgbe_log_sev_str(log_level) _ixgbe_log_sev_str(log_level).str
+
+ /**
+ * ixgbe_sysctl_fwlog_module_log_severity - Add tunables for a FW logging module
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: index to logging module
+ * @req: sysctl request pointer
+ */
+ static int
+ ixgbe_sysctl_fwlog_module_log_severity(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ struct sbuf *sbuf;
+ char *sev_str_end;
+ enum ixgbe_aci_fw_logging_mod module = (enum ixgbe_aci_fw_logging_mod)arg2;
+ int error, ll_num;
+ u8 log_level;
+ char sev_str[16];
+ bool sev_set = false;
+
+ log_level = cfg->module_entries[module].log_level;
+ sbuf = sbuf_new(NULL, sev_str, sizeof(sev_str), SBUF_FIXEDLEN);
+ sbuf_printf(sbuf, "%d<%s>", log_level, ixgbe_log_sev_str(log_level));
+ sbuf_finish(sbuf);
+ sbuf_delete(sbuf);
+
+ error = sysctl_handle_string(oidp, sev_str, sizeof(sev_str), req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_VERBOSE), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_VERBOSE;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_NORMAL), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_NORMAL;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_WARNING), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_WARNING;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_ERROR), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_ERROR;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_NONE), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_NONE;
+ sev_set = true;
+ }
+
+ if (!sev_set) {
+ ll_num = strtol(sev_str, &sev_str_end, 0);
+ if (sev_str_end == sev_str)
+ ll_num = -1;
+ if ((ll_num >= IXGBE_FWLOG_LEVEL_NONE) &&
+ (ll_num < IXGBE_FWLOG_LEVEL_INVALID))
+ log_level = ll_num;
+ else {
+ DEBUGOUT2("%s: \"%s\" is not a valid log level\n",
+ __func__, sev_str);
+ return (EINVAL);
+ }
+ }
+
+ cfg->module_entries[module].log_level = log_level;
+
+ return ixgbe_reconfig_fw_log(sc, cfg);
+ }
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_LOG_RESOLUTION \
+ "\nControl firmware message limit to send per ARQ event" \
+ "\t\nMin: 1" \
+ "\t\nMax: 128"
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_ARQ_ENA \
+ "\nControl whether to enable/disable reporting to admin Rx queue" \
+ "\n1 - Enable firmware reporting via ARQ" \
+ "\n0 - Disable firmware reporting via ARQ"
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_UART_ENA \
+ "\nControl whether to enable/disable reporting to UART" \
+ "\n1 - Enable firmware reporting via UART" \
+ "\n0 - Disable firmware reporting via UART"
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_ENABLE_ON_LOAD \
+ "\nControl whether to enable logging during the attach phase" \
+ "\n1 - Enable firmware logging during attach phase" \
+ "\n0 - Disable firmware logging during attach phase"
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_REGISTER \
+ "\nControl whether to enable/disable firmware logging" \
+ "\n1 - Enable firmware logging" \
+ "\n0 - Disable firmware logging"
+
+ #define IXGBE_SYSCTL_HELP_FWLOG_MODULE_SEVERITY \
+ "\nControl the level of log output messages for this module" \
+ "\n\tverbose <4> - Verbose messages + (Error|Warning|Normal)" \
+ "\n\tnormal <3> - Normal messages + (Error|Warning)" \
+ "\n\twarning <2> - Warning messages + (Error)" \
+ "\n\terror <1> - Error messages" \
+ "\n\tnone <0> - Disables all logging for this module"
+
+ /**
+ * ixgbe_fw_module_str - Convert a FW logging module to a string name
+ * @module: the module to convert
+ *
+ * Given a FW logging module id, convert it to a shorthand human readable
+ * name, for generating sysctl tunables.
+ */
+ static const char *
+ ixgbe_fw_module_str(enum ixgbe_aci_fw_logging_mod module)
+ {
+ switch (module) {
+ case IXGBE_ACI_FW_LOG_ID_GENERAL:
+ return "general";
+ case IXGBE_ACI_FW_LOG_ID_CTRL:
+ return "ctrl";
+ case IXGBE_ACI_FW_LOG_ID_LINK:
+ return "link";
+ case IXGBE_ACI_FW_LOG_ID_LINK_TOPO:
+ return "link_topo";
+ case IXGBE_ACI_FW_LOG_ID_DNL:
+ return "dnl";
+ case IXGBE_ACI_FW_LOG_ID_I2C:
+ return "i2c";
+ case IXGBE_ACI_FW_LOG_ID_SDP:
+ return "sdp";
+ case IXGBE_ACI_FW_LOG_ID_MDIO:
+ return "mdio";
+ case IXGBE_ACI_FW_LOG_ID_ADMINQ:
+ return "adminq";
+ case IXGBE_ACI_FW_LOG_ID_HDMA:
+ return "hdma";
+ case IXGBE_ACI_FW_LOG_ID_LLDP:
+ return "lldp";
+ case IXGBE_ACI_FW_LOG_ID_DCBX:
+ return "dcbx";
+ case IXGBE_ACI_FW_LOG_ID_DCB:
+ return "dcb";
+ case IXGBE_ACI_FW_LOG_ID_XLR:
+ return "xlr";
+ case IXGBE_ACI_FW_LOG_ID_NVM:
+ return "nvm";
+ case IXGBE_ACI_FW_LOG_ID_AUTH:
+ return "auth";
+ case IXGBE_ACI_FW_LOG_ID_VPD:
+ return "vpd";
+ case IXGBE_ACI_FW_LOG_ID_IOSF:
+ return "iosf";
+ case IXGBE_ACI_FW_LOG_ID_PARSER:
+ return "parser";
+ case IXGBE_ACI_FW_LOG_ID_SW:
+ return "sw";
+ case IXGBE_ACI_FW_LOG_ID_SCHEDULER:
+ return "scheduler";
+ case IXGBE_ACI_FW_LOG_ID_TXQ:
+ return "txq";
+ case IXGBE_ACI_FW_LOG_ID_ACL:
+ return "acl";
+ case IXGBE_ACI_FW_LOG_ID_POST:
+ return "post";
+ case IXGBE_ACI_FW_LOG_ID_WATCHDOG:
+ return "watchdog";
+ case IXGBE_ACI_FW_LOG_ID_TASK_DISPATCH:
+ return "task_dispatch";
+ case IXGBE_ACI_FW_LOG_ID_MNG:
+ return "mng";
+ case IXGBE_ACI_FW_LOG_ID_SYNCE:
+ return "synce";
+ case IXGBE_ACI_FW_LOG_ID_HEALTH:
+ return "health";
+ case IXGBE_ACI_FW_LOG_ID_TSDRV:
+ return "tsdrv";
+ case IXGBE_ACI_FW_LOG_ID_PFREG:
+ return "pfreg";
+ case IXGBE_ACI_FW_LOG_ID_MDLVER:
+ return "mdlver";
+ case IXGBE_ACI_FW_LOG_ID_MAX:
+ return "unknown";
+ }
+
+ /* The compiler generates errors on unhandled enum values if we omit
+ * the default case.
+ */
+ return "unknown";
+ }
+
+ /**
+ * ixgbe_add_fw_logging_tunables - Add tunables to configure FW logging events
+ * @sc: private softc structure
+ * @parent: parent node to add the tunables under
+ *
+ * Add tunables for configuring the firmware logging support. This includes
+ * a control to enable the logging, and controls for each module to configure
+ * which events to receive.
+ */
+ void
+ ixgbe_add_fw_logging_tunables(struct ixgbe_softc *sc, struct sysctl_oid *parent)
+ {
+ struct sysctl_oid_list *parent_list, *fwlog_list, *module_list;
+ struct sysctl_oid *fwlog_node, *module_node;
+ struct sysctl_ctx_list *ctx;
+ struct ixgbe_hw *hw = &sc->hw;
+ struct ixgbe_fwlog_cfg *cfg;
+ device_t dev = sc->dev;
+ enum ixgbe_aci_fw_logging_mod module;
+ u16 i;
+
+ cfg = &hw->fwlog_cfg;
+ ctx = device_get_sysctl_ctx(dev);
+ parent_list = SYSCTL_CHILDREN(parent);
+
+ fwlog_node = SYSCTL_ADD_NODE(ctx, parent_list, OID_AUTO, "fw_log",
+ CTLFLAG_RD, NULL,
+ "Firmware Logging");
+ fwlog_list = SYSCTL_CHILDREN(fwlog_node);
+
+ cfg->log_resolution = 10;
+ SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "log_resolution",
+ CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
+ 0, ixgbe_sysctl_fwlog_log_resolution,
+ "CU", IXGBE_SYSCTL_HELP_FWLOG_LOG_RESOLUTION);
+
+ cfg->options |= IXGBE_FWLOG_OPTION_ARQ_ENA;
+ SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "arq_en",
+ CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
+ IXGBE_FWLOG_OPTION_ARQ_ENA, ixgbe_sysctl_fwlog_set_cfg_options,
+ "CU", IXGBE_SYSCTL_HELP_FWLOG_ARQ_ENA);
+
+ SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "uart_en",
+ CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
+ IXGBE_FWLOG_OPTION_UART_ENA, ixgbe_sysctl_fwlog_set_cfg_options,
+ "CU", IXGBE_SYSCTL_HELP_FWLOG_UART_ENA);
+
+ SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "on_load",
+ CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
+ IXGBE_FWLOG_OPTION_REGISTER_ON_INIT, ixgbe_sysctl_fwlog_set_cfg_options,
+ "CU", IXGBE_SYSCTL_HELP_FWLOG_ENABLE_ON_LOAD);
+
+ SYSCTL_ADD_PROC(ctx, fwlog_list, OID_AUTO, "register",
+ CTLTYPE_U8 | CTLFLAG_RWTUN, sc,
+ 0, ixgbe_sysctl_fwlog_register,
+ "CU", IXGBE_SYSCTL_HELP_FWLOG_REGISTER);
+
+ module_node = SYSCTL_ADD_NODE(ctx, fwlog_list, OID_AUTO, "severity",
+ CTLFLAG_RD, NULL,
+ "Level of log output");
+
+ module_list = SYSCTL_CHILDREN(module_node);
+
+ for (i = 0; i < IXGBE_ACI_FW_LOG_ID_MAX; i++) {
+ /* Setup some defaults */
+ cfg->module_entries[i].module_id = i;
+ cfg->module_entries[i].log_level = IXGBE_FWLOG_LEVEL_NONE;
+ module = (enum ixgbe_aci_fw_logging_mod)i;
+
+ SYSCTL_ADD_PROC(ctx, module_list,
+ OID_AUTO, ixgbe_fw_module_str(module),
+ CTLTYPE_STRING | CTLFLAG_RWTUN, sc,
+ module, ixgbe_sysctl_fwlog_module_log_severity,
+ "A", IXGBE_SYSCTL_HELP_FWLOG_MODULE_SEVERITY);
+ }
+ }
+ \ No newline at end of file
diff --git a/sys/dev/ixgbe/ixgbe_osdep.c b/sys/dev/ixgbe/ixgbe_osdep.c
index 9bd9ce63b786..d96e15f4f87f 100644
--- a/sys/dev/ixgbe/ixgbe_osdep.c
+++ b/sys/dev/ixgbe/ixgbe_osdep.c
@@ -140,3 +140,39 @@ ixgbe_destroy_lock(struct ixgbe_lock *lock)
if (mtx_initialized(&lock->mutex))
mtx_destroy(&lock->mutex);
}
+
+/**
+ * ixgbe_info_fwlog - Format and print an array of values to the console
+ * @hw: private hardware structure
+ * @rowsize: preferred number of rows to use
+ * @groupsize: preferred size in bytes to print each chunk
+ * @buf: the array buffer to print
+ * @len: size of the array buffer
+ *
+ * Format the given array as a series of uint8_t values with hexadecimal
+ * notation and log the contents to the console log. This variation is
+ * specific to firmware logging.
+ *
+ * TODO: Currently only supports a group size of 1, due to the way hexdump is
+ * implemented.
+ */
+void
+ixgbe_info_fwlog(struct ixgbe_hw *hw, uint32_t rowsize, uint32_t __unused groupsize,
+ uint8_t *buf, size_t len)
+{
+ device_t dev = ((struct ixgbe_softc *)hw->back)->dev;
+ char prettyname[20];
+
+ if (!ixgbe_fwlog_supported(hw))
+ return;
+
+ /* Format the device header to a string */
+ snprintf(prettyname, sizeof(prettyname), "%s: FWLOG: ",
+ device_get_nameunit(dev));
+
+ /* Make sure the row-size isn't too large */
+ if (rowsize > 0xFF)
+ rowsize = 0xFF;
+
+ hexdump(buf, len, prettyname, HD_OMIT_CHARS | rowsize);
+}
diff --git a/sys/dev/ixgbe/ixgbe_osdep.h b/sys/dev/ixgbe/ixgbe_osdep.h
index 8cf1d13736ce..ec2b3274ec31 100644
--- a/sys/dev/ixgbe/ixgbe_osdep.h
+++ b/sys/dev/ixgbe/ixgbe_osdep.h
@@ -253,4 +253,7 @@ ixgbe_free(struct ixgbe_hw __unused *hw, void *addr)
free(addr, M_DEVBUF);
}
+void ixgbe_info_fwlog(struct ixgbe_hw *hw, uint32_t rowsize,
+ uint32_t groupsize, uint8_t *buf, size_t len);
+
#endif /* _IXGBE_OSDEP_H_ */
diff --git a/sys/dev/ixgbe/ixgbe_sriov.h b/sys/dev/ixgbe/ixgbe_sriov.h
index e5a78a7220cc..3c456ee819f2 100644
--- a/sys/dev/ixgbe/ixgbe_sriov.h
+++ b/sys/dev/ixgbe/ixgbe_sriov.h
@@ -94,7 +94,7 @@ u32 ixgbe_get_mrqc(int);
#define ixgbe_align_all_queue_indices(_a)
#define ixgbe_vf_que_index(_a, _b, _c) (_c)
#define ixgbe_get_mtqc(_a) IXGBE_MTQC_64Q_1PB
-#define ixgbe_get_mrqc(_a) 0
+#define ixgbe_get_mrqc(_a) IXGBE_MRQC_RSSEN
#endif /* PCI_IOV */
diff --git a/sys/dev/ixgbe/ixgbe_x540.c b/sys/dev/ixgbe/ixgbe_x540.c
index 57cec5b52e18..561fe6f0f78a 100644
--- a/sys/dev/ixgbe/ixgbe_x540.c
+++ b/sys/dev/ixgbe/ixgbe_x540.c
@@ -878,7 +878,21 @@ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask)
IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC_BY_MAC(hw), swfw_sync);
ixgbe_release_swfw_sync_semaphore(hw);
- msec_delay(2);
+
+ /*
+ * EEPROM / flash access requires a 2ms sleep or interacting with
+ * them isn't stable. However, a 2ms delay for all sync operations
+ * is very expensive for MDIO access.
+ *
+ * So use a 10us delay for PHY0/PHY1 MDIO and management access and
+ * 2ms for everything else. This keep MDIO access (eg from a switch
+ * driver) fast.
+ */
+ if (mask &
+ (IXGBE_GSSR_PHY0_SM | IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_SW_MNG_SM))
+ usec_delay(10);
+ else
+ usec_delay(2000);
}
/**
diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
index fb8b79c8f787..9bcb0dcf8e16 100644
--- a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
+++ b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
@@ -1135,6 +1135,25 @@ mlx5e_hw_clock(struct mlx5e_priv *priv)
}
/*
+ * Seed the first calibration point so that base_prev and clbr_hw_prev
+ * are always valid. Called once during attach before the first
+ * calibration callout fires.
+ */
+static void
+mlx5e_seed_calibration(struct mlx5e_priv *priv)
+{
+ struct mlx5e_clbr_point *cp;
+ struct timespec ts;
+
+ cp = &priv->clbr_points[0];
+ cp->clbr_hw_curr = mlx5e_hw_clock(priv);
+ nanouptime(&ts);
+ cp->base_curr = mlx5e_timespec2usec(&ts);
+ cp->clbr_hw_prev = cp->clbr_hw_curr - 1;
+ cp->base_prev = cp->base_curr - 1;
+}
+
+/*
* The calibration callout, it runs either in the context of the
* thread which enables calibration, or in callout. It takes the
* snapshot of system and adapter clocks, then advances the pointers to
@@ -1147,6 +1166,9 @@ mlx5e_calibration_callout(void *arg)
struct mlx5e_priv *priv;
struct mlx5e_clbr_point *next, *curr;
struct timespec ts;
+ uint64_t hw_delta_new, hw_delta_old;
+ uint64_t old_nsec, old_projected, old_sec;
+ uint64_t res_n, res_s, res_s_mod, rt_delta_old;
int clbr_curr_next;
priv = arg;
@@ -1175,6 +1197,33 @@ mlx5e_calibration_callout(void *arg)
nanouptime(&ts);
next->base_curr = mlx5e_timespec2usec(&ts);
+ /*
+ * Ensure monotonicity across calibration transitions. Compute
+ * what the old calibration would extrapolate to at the new
+ * hw_curr. If the new base_curr is less, clamp it so the new
+ * slope is at least as steep as the old one. This prevents
+ * packets from seeing time go backwards when the slope drops.
+ *
+ * Use the same split-seconds technique as mlx5e_mbuf_tstmp()
+ * to avoid overflowing uint64_t in the multiplication.
+ */
+ hw_delta_new = next->clbr_hw_curr - curr->clbr_hw_curr;
+ rt_delta_old = curr->base_curr - curr->base_prev;
+ hw_delta_old = curr->clbr_hw_curr - curr->clbr_hw_prev;
+ old_sec = hw_delta_new / priv->cclk;
+ old_nsec = hw_delta_new % priv->cclk;
+ res_s = old_sec * rt_delta_old;
+ res_n = old_nsec * rt_delta_old;
+ res_s_mod = res_s % hw_delta_old;
+ res_s /= hw_delta_old;
+ res_s_mod *= priv->cclk;
+ res_n += res_s_mod;
+ res_n /= hw_delta_old;
+ res_s *= priv->cclk;
+ old_projected = curr->base_curr + res_s + res_n;
+ if (next->base_curr < old_projected)
+ next->base_curr = old_projected;
+
curr->clbr_gen = 0;
atomic_thread_fence_rel();
priv->clbr_curr = clbr_curr_next;
@@ -4887,6 +4936,7 @@ mlx5e_create_ifp(struct mlx5_core_dev *mdev)
callout_init(&priv->tstmp_clbr, 1);
/* Pull out the frequency of the clock in hz */
priv->cclk = (uint64_t)MLX5_CAP_GEN(mdev, device_frequency_khz) * 1000ULL;
+ mlx5e_seed_calibration(priv);
mlx5e_reset_calibration_callout(priv);
pa.pa_version = PFIL_VERSION;
diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c
index b75033300061..753a8b380a75 100644
--- a/sys/dev/nvme/nvme_ctrlr.c
+++ b/sys/dev/nvme/nvme_ctrlr.c
@@ -1346,29 +1346,51 @@ nvme_ctrlr_shared_handler(void *arg)
#define NVME_MAX_PAGES (int)(1024 / sizeof(vm_page_t))
static int
+nvme_page_count(vm_offset_t start, size_t len)
+{
+ return atop(round_page(start + len) - trunc_page(start));
+}
+
+static int
nvme_user_ioctl_req(vm_offset_t addr, size_t len, bool is_read,
- vm_page_t *upages, int max_pages, int *npagesp, struct nvme_request **req,
+ vm_page_t **upages, int max_pages, int *npagesp, struct nvme_request **req,
nvme_cb_fn_t cb_fn, void *cb_arg)
{
vm_prot_t prot = VM_PROT_READ;
- int err;
+ int err, npages;
+ vm_page_t *upages_us;
+
+ upages_us = *upages;
+ npages = nvme_page_count(addr, len);
+ if (npages > atop(maxphys))
+ return (EINVAL);
+ if (npages > max_pages)
+ upages_us = malloc(npages * sizeof(vm_page_t), M_NVME,
+ M_ZERO | M_WAITOK);
if (is_read)
prot |= VM_PROT_WRITE; /* Device will write to host memory */
err = vm_fault_hold_pages(&curproc->p_vmspace->vm_map,
- addr, len, prot, upages, max_pages, npagesp);
- if (err != 0)
+ addr, len, prot, upages_us, npages, npagesp);
+ if (err != 0) {
+ if (*upages != upages_us)
+ free(upages_us, M_NVME);
return (err);
+ }
*req = nvme_allocate_request_null(M_WAITOK, cb_fn, cb_arg);
- (*req)->payload = memdesc_vmpages(upages, len, addr & PAGE_MASK);
+ (*req)->payload = memdesc_vmpages(upages_us, len, addr & PAGE_MASK);
(*req)->payload_valid = true;
+ if (*upages != upages_us)
+ *upages = upages_us;
return (0);
}
static void
-nvme_user_ioctl_free(vm_page_t *pages, int npage)
+nvme_user_ioctl_free(vm_page_t *pages, int npage, bool freeit)
{
vm_page_unhold_pages(pages, npage);
+ if (freeit)
+ free(pages, M_NVME);
}
static void
@@ -1400,7 +1422,8 @@ nvme_ctrlr_passthrough_cmd(struct nvme_controller *ctrlr,
struct mtx *mtx;
int ret = 0;
int npages = 0;
- vm_page_t upages[NVME_MAX_PAGES];
+ vm_page_t upages_small[NVME_MAX_PAGES];
+ vm_page_t *upages = upages_small;
if (pt->len > 0) {
if (pt->len > ctrlr->max_xfer_size) {
@@ -1411,7 +1434,7 @@ nvme_ctrlr_passthrough_cmd(struct nvme_controller *ctrlr,
}
if (is_user) {
ret = nvme_user_ioctl_req((vm_offset_t)pt->buf, pt->len,
- pt->is_read, upages, nitems(upages), &npages, &req,
+ pt->is_read, &upages, nitems(upages_small), &npages, &req,
nvme_pt_done, pt);
if (ret != 0)
return (ret);
@@ -1449,7 +1472,7 @@ nvme_ctrlr_passthrough_cmd(struct nvme_controller *ctrlr,
mtx_unlock(mtx);
if (npages > 0)
- nvme_user_ioctl_free(upages, npages);
+ nvme_user_ioctl_free(upages, npages, upages != upages_small);
return (ret);
}
@@ -1477,7 +1500,8 @@ nvme_ctrlr_linux_passthru_cmd(struct nvme_controller *ctrlr,
struct mtx *mtx;
int ret = 0;
int npages = 0;
- vm_page_t upages[NVME_MAX_PAGES];
+ vm_page_t upages_small[NVME_MAX_PAGES];
+ vm_page_t *upages = upages_small;
/*
* We don't support metadata.
@@ -1494,8 +1518,8 @@ nvme_ctrlr_linux_passthru_cmd(struct nvme_controller *ctrlr,
}
if (is_user) {
ret = nvme_user_ioctl_req(npc->addr, npc->data_len,
- npc->opcode & 0x1, upages, nitems(upages), &npages,
- &req, nvme_npc_done, npc);
+ npc->opcode & 0x1, &upages, nitems(upages_small),
+ &npages, &req, nvme_npc_done, npc);
if (ret != 0)
return (ret);
} else
@@ -1533,7 +1557,7 @@ nvme_ctrlr_linux_passthru_cmd(struct nvme_controller *ctrlr,
mtx_unlock(mtx);
if (npages > 0)
- nvme_user_ioctl_free(upages, npages);
+ nvme_user_ioctl_free(upages, npages, upages != upages_small);
return (ret);
}
diff --git a/sys/dev/ofw/openfirm.c b/sys/dev/ofw/openfirm.c
index b5f58b86a9c3..a8d3786ed152 100644
--- a/sys/dev/ofw/openfirm.c
+++ b/sys/dev/ofw/openfirm.c
@@ -381,11 +381,11 @@ OF_getproplen(phandle_t package, const char *propname)
}
/* Check existence of a property of a package. */
-int
+bool
OF_hasprop(phandle_t package, const char *propname)
{
- return (OF_getproplen(package, propname) >= 0 ? 1 : 0);
+ return (OF_getproplen(package, propname) >= 0);
}
/* Get the value of a property of a package. */
diff --git a/sys/dev/ofw/openfirm.h b/sys/dev/ofw/openfirm.h
index 4e2b035827cb..fd13f4abd29e 100644
--- a/sys/dev/ofw/openfirm.h
+++ b/sys/dev/ofw/openfirm.h
@@ -108,7 +108,7 @@ ssize_t OF_getprop(phandle_t node, const char *propname, void *buf,
size_t len);
ssize_t OF_getencprop(phandle_t node, const char *prop, pcell_t *buf,
size_t len); /* Same as getprop, but maintains endianness */
-int OF_hasprop(phandle_t node, const char *propname);
+bool OF_hasprop(phandle_t node, const char *propname);
ssize_t OF_searchprop(phandle_t node, const char *propname, void *buf,
size_t len);
ssize_t OF_searchencprop(phandle_t node, const char *propname,
diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h
index f6aaf30611e4..3ec7879b8a09 100644
--- a/sys/dev/pci/pcireg.h
+++ b/sys/dev/pci/pcireg.h
@@ -464,6 +464,7 @@
#define PCIP_SERIALBUS_USB_OHCI 0x10
#define PCIP_SERIALBUS_USB_EHCI 0x20
#define PCIP_SERIALBUS_USB_XHCI 0x30
+#define PCIP_SERIALBUS_USB_USB4 0x40
#define PCIP_SERIALBUS_USB_DEVICE 0xfe
#define PCIS_SERIALBUS_FC 0x04
#define PCIS_SERIALBUS_SMBUS 0x05
diff --git a/sys/dev/qcom_clk/qcom_clk_rcg2.c b/sys/dev/qcom_clk/qcom_clk_rcg2.c
index 0407706dd138..6a1962982184 100644
--- a/sys/dev/qcom_clk/qcom_clk_rcg2.c
+++ b/sys/dev/qcom_clk/qcom_clk_rcg2.c
@@ -370,7 +370,7 @@ qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
device_printf(clknode_get_device(sc->clknode),
"%s: no suitable freqtbl entry found for freq %llu\n",
__func__,
- *fout);
+ (unsigned long long) *fout);
return (ERANGE);
}
@@ -475,7 +475,7 @@ qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
*fout,
f->parent,
f->freq,
- p_freq);
+ (unsigned long long) p_freq);
/*
* To ensure glitch-free operation on some clocks, set it to
@@ -547,7 +547,7 @@ qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
"%llu\n",
__func__,
f->parent,
- p_freq);
+ (unsigned long long) p_freq);
return (ENXIO);
}
@@ -570,7 +570,7 @@ qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
*fout,
f->freq,
f->parent,
- p_freq);
+ (unsigned long long) p_freq);
/*
* Set the parent node, the parent programming and the divisor
diff --git a/sys/dev/qcom_gcc/qcom_gcc_clock.c b/sys/dev/qcom_gcc/qcom_gcc_clock.c
index c8c10b0c5172..f51b4021a821 100644
--- a/sys/dev/qcom_gcc/qcom_gcc_clock.c
+++ b/sys/dev/qcom_gcc/qcom_gcc_clock.c
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2025, Adrian Chadd <adrian@FreeBSD.org>
+ * Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,6 +37,7 @@
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
+#include <sys/rman.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
diff --git a/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c b/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c
index f99d1d9ad9f1..127ca944c77e 100644
--- a/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c
+++ b/sys/dev/qcom_gcc/qcom_gcc_ipq4018_reset.c
@@ -38,6 +38,7 @@
#include <machine/bus.h>
#include <machine/resource.h>
+#include <sys/rman.h>
#include <sys/bus.h>
#include <dev/fdt/fdt_common.h>
@@ -136,7 +137,8 @@ qcom_gcc_ipq4018_hwreset_assert(device_t dev, intptr_t id, bool reset)
sc = device_get_softc(dev);
if (id > nitems(gcc_ipq4019_reset_list)) {
- device_printf(dev, "%s: invalid id (%d)\n", __func__, id);
+ device_printf(dev, "%s: invalid id (%d)\n", __func__,
+ (uint32_t) id);
return (EINVAL);
}
@@ -160,7 +162,8 @@ qcom_gcc_ipq4018_hwreset_is_asserted(device_t dev, intptr_t id, bool *reset)
sc = device_get_softc(dev);
if (id > nitems(gcc_ipq4019_reset_list)) {
- device_printf(dev, "%s: invalid id (%d)\n", __func__, id);
+ device_printf(dev, "%s: invalid id (%d)\n", __func__,
+ (uint32_t) id);
return (EINVAL);
}
mtx_lock(&sc->mtx);
@@ -171,7 +174,7 @@ qcom_gcc_ipq4018_hwreset_is_asserted(device_t dev, intptr_t id, bool *reset)
*reset = false;
mtx_unlock(&sc->mtx);
- device_printf(dev, "called; id=%d\n", id);
+ device_printf(dev, "called; id=%d\n", (uint32_t) id);
return (0);
}
diff --git a/sys/dev/qcom_gcc/qcom_gcc_main.c b/sys/dev/qcom_gcc/qcom_gcc_main.c
index 3950bd985feb..38f409827541 100644
--- a/sys/dev/qcom_gcc/qcom_gcc_main.c
+++ b/sys/dev/qcom_gcc/qcom_gcc_main.c
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2025, Adrian Chadd <adrian@FreeBSD.org>
+ * Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -39,6 +39,7 @@
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
+#include <sys/rman.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
@@ -51,6 +52,7 @@
#include "qcom_gcc_var.h"
#include "qcom_gcc_ipq4018.h"
+#include "qcom_gcc_msm8916.h"
static int qcom_gcc_modevent(module_t, int, void *);
@@ -67,6 +69,8 @@ struct qcom_gcc_chipset_list_entry {
static struct qcom_gcc_chipset_list_entry qcom_gcc_chipset_list[] = {
{ "qcom,gcc-ipq4019", "Qualcomm IPQ4018 Clock/Reset Controller",
QCOM_GCC_CHIPSET_IPQ4018 },
+ { "qcom,gcc-msm8916", "Qualcomm MSM8916 Clock/Reset Controller",
+ QCOM_GCC_CHIPSET_MSM8916 },
{ NULL, NULL, 0 },
};
@@ -135,6 +139,10 @@ qcom_gcc_attach(device_t dev)
qcom_gcc_ipq4018_hwreset_init(sc);
mem_sz = 0x60000;
break;
+ case QCOM_GCC_CHIPSET_MSM8916:
+ qcom_gcc_msm8916_hwreset_init(sc);
+ mem_sz = 0x0;
+ break;
case QCOM_GCC_CHIPSET_NONE:
device_printf(dev, "Invalid chipset (%d)\n", sc->sc_chipset);
return (ENXIO);
@@ -142,8 +150,13 @@ qcom_gcc_attach(device_t dev)
sc->reg_rid = 0;
- sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
- &sc->reg_rid, mem_sz, RF_ACTIVE);
+ if (mem_sz != 0)
+ sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
+ &sc->reg_rid, mem_sz, RF_ACTIVE);
+ else
+ sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->reg_rid, RF_ACTIVE);
+
if (sc->reg == NULL) {
device_printf(dev, "Couldn't allocate memory resource!\n");
return (ENXIO);
@@ -163,6 +176,9 @@ qcom_gcc_attach(device_t dev)
case QCOM_GCC_CHIPSET_IPQ4018:
qcom_gcc_ipq4018_clock_setup(sc);
break;
+ case QCOM_GCC_CHIPSET_MSM8916:
+ qcom_gcc_msm8916_clock_setup(sc);
+ break;
case QCOM_GCC_CHIPSET_NONE:
device_printf(dev, "Invalid chipset (%d)\n", sc->sc_chipset);
return (ENXIO);
diff --git a/sys/dev/qcom_gcc/qcom_gcc_msm8916.h b/sys/dev/qcom_gcc/qcom_gcc_msm8916.h
new file mode 100644
index 000000000000..10758b0744a4
--- /dev/null
+++ b/sys/dev/qcom_gcc/qcom_gcc_msm8916.h
@@ -0,0 +1,41 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __QCOM_GCC_MSM8916_H__
+#define __QCOM_GCC_MSM8916_H__
+
+/*
+ * reset block
+ */
+extern void qcom_gcc_msm8916_hwreset_init(struct qcom_gcc_softc *);
+
+/*
+ * clock block
+ */
+extern void qcom_gcc_msm8916_clock_setup(struct qcom_gcc_softc *);
+
+#endif /* __QCOM_GCC_MSM8916_H__ */
diff --git a/sys/arm64/qualcomm/qcom_gcc.c b/sys/dev/qcom_gcc/qcom_gcc_msm8916_clock.c
index 3f08577f4d3c..5c0e4afcc17f 100644
--- a/sys/arm64/qualcomm/qcom_gcc.c
+++ b/sys/dev/qcom_gcc/qcom_gcc_msm8916_clock.c
@@ -41,6 +41,9 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#include "qcom_gcc_var.h"
+#include "qcom_gcc_msm8916.h"
+
#define GCC_QDSS_BCR 0x29000
#define GCC_QDSS_BCR_BLK_ARES (1 << 0) /* Async software reset. */
#define GCC_QDSS_CFG_AHB_CBCR 0x29008
@@ -50,94 +53,32 @@
#define GCC_QDSS_DAP_CBCR 0x29084
#define DAP_CBCR_CLK_ENABLE (1 << 0) /* DAP clk branch ctrl */
-static struct ofw_compat_data compat_data[] = {
- { "qcom,gcc-msm8916", 1 },
- { NULL, 0 }
-};
-
-struct qcom_gcc_softc {
- struct resource *res;
-};
-
-static struct resource_spec qcom_gcc_spec[] = {
- { SYS_RES_MEMORY, 0, RF_ACTIVE },
- { -1, 0 }
-};
-
/*
* Qualcomm Debug Subsystem (QDSS)
* block enabling routine.
*/
static void
-qcom_qdss_enable(struct qcom_gcc_softc *sc)
+qcom_msm8916_qdss_enable(struct qcom_gcc_softc *sc)
{
/* Put QDSS block to reset */
- bus_write_4(sc->res, GCC_QDSS_BCR, GCC_QDSS_BCR_BLK_ARES);
+ bus_write_4(sc->reg, GCC_QDSS_BCR, GCC_QDSS_BCR_BLK_ARES);
/* Enable AHB clock branch */
- bus_write_4(sc->res, GCC_QDSS_CFG_AHB_CBCR, AHB_CBCR_CLK_ENABLE);
+ bus_write_4(sc->reg, GCC_QDSS_CFG_AHB_CBCR, AHB_CBCR_CLK_ENABLE);
/* Enable DAP clock branch */
- bus_write_4(sc->res, GCC_QDSS_DAP_CBCR, DAP_CBCR_CLK_ENABLE);
+ bus_write_4(sc->reg, GCC_QDSS_DAP_CBCR, DAP_CBCR_CLK_ENABLE);
/* Enable ETR USB clock branch */
- bus_write_4(sc->res, GCC_QDSS_ETR_USB_CBCR, ETR_USB_CBCR_CLK_ENABLE);
+ bus_write_4(sc->reg, GCC_QDSS_ETR_USB_CBCR, ETR_USB_CBCR_CLK_ENABLE);
/* Out of reset */
- bus_write_4(sc->res, GCC_QDSS_BCR, 0);
-}
-
-static int
-qcom_gcc_probe(device_t dev)
-{
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
-
- if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
- return (ENXIO);
-
- device_set_desc(dev, "Qualcomm Global Clock Controller");
-
- return (BUS_PROBE_DEFAULT);
+ bus_write_4(sc->reg, GCC_QDSS_BCR, 0);
}
-static int
-qcom_gcc_attach(device_t dev)
+void
+qcom_gcc_msm8916_clock_setup(struct qcom_gcc_softc *sc)
{
- struct qcom_gcc_softc *sc;
-
- sc = device_get_softc(dev);
-
- if (bus_alloc_resources(dev, qcom_gcc_spec, &sc->res) != 0) {
- device_printf(dev, "cannot allocate resources for device\n");
- return (ENXIO);
- }
-
- /*
- * Enable debug unit.
- * This is required for Coresight operation.
- * This also enables USB clock branch.
- */
- qcom_qdss_enable(sc);
-
- return (0);
+ qcom_msm8916_qdss_enable(sc);
}
-
-static device_method_t qcom_gcc_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, qcom_gcc_probe),
- DEVMETHOD(device_attach, qcom_gcc_attach),
-
- DEVMETHOD_END
-};
-
-static driver_t qcom_gcc_driver = {
- "qcom_gcc",
- qcom_gcc_methods,
- sizeof(struct qcom_gcc_softc),
-};
-
-EARLY_DRIVER_MODULE(qcom_gcc, simplebus, qcom_gcc_driver, 0, 0,
- BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
-MODULE_VERSION(qcom_gcc, 1);
diff --git a/sys/dev/qcom_gcc/qcom_gcc_msm8916_reset.c b/sys/dev/qcom_gcc/qcom_gcc_msm8916_reset.c
new file mode 100644
index 000000000000..c83fd3e981ab
--- /dev/null
+++ b/sys/dev/qcom_gcc/qcom_gcc_msm8916_reset.c
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sglist.h>
+#include <sys/random.h>
+#include <sys/stdatomic.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/hwreset/hwreset.h>
+
+#include "hwreset_if.h"
+
+#include "qcom_gcc_var.h"
+#include "qcom_gcc_msm8916.h"
+
+static int
+qcom_gcc_msm8916_hwreset_assert(device_t dev, intptr_t id, bool reset)
+{
+ device_printf(dev, "%s: invalid id (%d)\n", __func__, (uint32_t) id);
+ return (EINVAL);
+}
+
+static int
+qcom_gcc_msm8916_hwreset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+ device_printf(dev, "%s: invalid id (%d)\n", __func__, (uint32_t) id);
+ return (EINVAL);
+}
+
+void
+qcom_gcc_msm8916_hwreset_init(struct qcom_gcc_softc *sc)
+{
+ sc->sc_cb.hw_reset_assert = qcom_gcc_msm8916_hwreset_assert;
+ sc->sc_cb.hw_reset_is_asserted = qcom_gcc_msm8916_hwreset_is_asserted;
+}
diff --git a/sys/dev/qcom_gcc/qcom_gcc_var.h b/sys/dev/qcom_gcc/qcom_gcc_var.h
index 2d4e969e1134..e3796f0d5f0f 100644
--- a/sys/dev/qcom_gcc/qcom_gcc_var.h
+++ b/sys/dev/qcom_gcc/qcom_gcc_var.h
@@ -31,6 +31,7 @@
typedef enum {
QCOM_GCC_CHIPSET_NONE = 0,
QCOM_GCC_CHIPSET_IPQ4018 = 1,
+ QCOM_GCC_CHIPSET_MSM8916 = 2,
} qcom_gcc_chipset_t;
struct qcom_gcc_reset_entry {
diff --git a/sys/dev/rge/if_rge.c b/sys/dev/rge/if_rge.c
index 0007b07e0fa6..8887e8d39ae4 100644
--- a/sys/dev/rge/if_rge.c
+++ b/sys/dev/rge/if_rge.c
@@ -103,12 +103,7 @@ static void rge_tx_task(void *, int);
static void rge_txq_flush_mbufs(struct rge_softc *sc);
static void rge_tick(void *);
static void rge_link_state(struct rge_softc *);
-#if 0
-#ifndef SMALL_KERNEL
-int rge_wol(struct ifnet *, int);
-void rge_wol_power(struct rge_softc *);
-#endif
-#endif
+static void rge_setwol(struct rge_softc *);
struct rge_matchid {
uint16_t vendor;
@@ -161,7 +156,11 @@ rge_attach_if(struct rge_softc *sc, const char *eaddr)
if_setcapabilities(sc->sc_ifp, IFCAP_HWCSUM);
if_setcapenable(sc->sc_ifp, if_getcapabilities(sc->sc_ifp));
- /* TODO: set WOL */
+ /* Enable WOL if PM is supported. */
+ if (pci_has_pm(sc->sc_dev)) {
+ if_setcapabilitiesbit(sc->sc_ifp, IFCAP_WOL_MAGIC, 0);
+ if_setcapenablebit(sc->sc_ifp, IFCAP_WOL_MAGIC, 0);
+ }
/* Attach interface */
ether_ifattach(sc->sc_ifp, eaddr);
@@ -446,23 +445,19 @@ rge_attach(device_t dev)
rge_config_imtype(sc, RGE_IMTYPE_SIM);
- /* TODO: disable ASPM/ECPM? */
-
-#if 0
- /*
- * PCI Express check.
- */
- if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
- &offset, NULL)) {
- /* Disable PCIe ASPM and ECPM. */
- reg = pci_conf_read(pa->pa_pc, pa->pa_tag,
- offset + PCI_PCIE_LCSR);
- reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1 |
- PCI_PCIE_LCSR_ECPM);
- pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PCIE_LCSR,
- reg);
+ /* Disable PCIe ASPM and ECPM if requested. */
+ if (sc->sc_disable_aspm) {
+ int ecap;
+ if (pci_find_cap(dev, PCIY_EXPRESS, &ecap) == 0) {
+ uint16_t lctl;
+ lctl = pci_read_config(dev,
+ ecap + PCIER_LINK_CTL, 2);
+ lctl &= ~(PCIEM_LINK_CTL_ASPMC |
+ PCIEM_LINK_CTL_ECPM);
+ pci_write_config(dev,
+ ecap + PCIER_LINK_CTL, lctl, 2);
+ }
}
-#endif
RGE_LOCK(sc);
if (rge_chipinit(sc)) {
@@ -654,26 +649,6 @@ rge_detach(device_t dev)
return (0);
}
-#if 0
-
-int
-rge_activate(struct device *self, int act)
-{
-#ifndef SMALL_KERNEL
- struct rge_softc *sc = (struct rge_softc *)self;
-#endif
-
- switch (act) {
- case DVACT_POWERDOWN:
-#ifndef SMALL_KERNEL
- rge_wol_power(sc);
-#endif
- break;
- }
- return (0);
-}
-#endif
-
static void
rge_intr_msi(void *arg)
{
@@ -1014,7 +989,9 @@ rge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
reinit = 1;
}
- /* TODO: WOL */
+ if ((mask & IFCAP_WOL_MAGIC) != 0 &&
+ (if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) != 0)
+ if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
if ((mask & IFCAP_RXCSUM) != 0 &&
(if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0) {
@@ -2620,6 +2597,22 @@ rge_link_state(struct rge_softc *sc)
}
}
+static void
+rge_setwol(struct rge_softc *sc)
+{
+ if_t ifp = sc->sc_ifp;
+ int enable;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ if (!pci_has_pm(sc->sc_dev))
+ return;
+
+ enable = (if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0;
+
+ rge_wol_config(sc, enable);
+}
+
/**
* @brief Suspend
*/
@@ -2630,7 +2623,7 @@ rge_suspend(device_t dev)
RGE_LOCK(sc);
rge_stop_locked(sc);
- /* TODO: wake on lan */
+ rge_setwol(sc);
sc->sc_suspended = true;
RGE_UNLOCK(sc);
@@ -2646,7 +2639,6 @@ rge_resume(device_t dev)
struct rge_softc *sc = device_get_softc(dev);
RGE_LOCK(sc);
- /* TODO: wake on lan */
/* reinit if required */
if (if_getflags(sc->sc_ifp) & IFF_UP)
@@ -2669,6 +2661,7 @@ rge_shutdown(device_t dev)
RGE_LOCK(sc);
rge_stop_locked(sc);
+ rge_setwol(sc);
RGE_UNLOCK(sc);
return (0);
diff --git a/sys/dev/rge/if_rge_hw.c b/sys/dev/rge/if_rge_hw.c
index 35a0e93dd193..ba01e389af14 100644
--- a/sys/dev/rge/if_rge_hw.c
+++ b/sys/dev/rge/if_rge_hw.c
@@ -2196,50 +2196,37 @@ rge_get_link_status(struct rge_softc *sc)
return ((RGE_READ_2(sc, RGE_PHYSTAT) & RGE_PHYSTAT_LINK) ? 1 : 0);
}
-#if 0
-#ifndef SMALL_KERNEL
-int
-rge_wol(struct ifnet *ifp, int enable)
+void
+rge_wol_config(struct rge_softc *sc, int enable)
{
- struct rge_softc *sc = ifp->if_softc;
-
- if (enable) {
- if (!(RGE_READ_1(sc, RGE_CFG1) & RGE_CFG1_PM_EN)) {
- printf("%s: power management is disabled, "
- "cannot do WOL\n", sc->sc_dev.dv_xname);
- return (ENOTSUP);
- }
-
- }
-
- rge_iff(sc);
-
if (enable)
RGE_MAC_SETBIT(sc, 0xc0b6, 0x0001);
else
RGE_MAC_CLRBIT(sc, 0xc0b6, 0x0001);
+ /* Enable config register write. */
RGE_SETBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
- RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE | RGE_CFG5_WOL_UCAST |
- RGE_CFG5_WOL_MCAST | RGE_CFG5_WOL_BCAST);
+
+ /* Clear all WOL bits, then set as requested. */
RGE_CLRBIT_1(sc, RGE_CFG3, RGE_CFG3_WOL_LINK | RGE_CFG3_WOL_MAGIC);
- if (enable)
+ RGE_CLRBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE |
+ RGE_CFG5_WOL_UCAST | RGE_CFG5_WOL_MCAST | RGE_CFG5_WOL_BCAST);
+ if (enable) {
+ RGE_SETBIT_1(sc, RGE_CFG3, RGE_CFG3_WOL_MAGIC);
RGE_SETBIT_1(sc, RGE_CFG5, RGE_CFG5_WOL_LANWAKE);
- RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
+ }
- return (0);
-}
+ /* Config register write done. */
+ RGE_CLRBIT_1(sc, RGE_EECMD, RGE_EECMD_WRITECFG);
-void
-rge_wol_power(struct rge_softc *sc)
-{
- /* Disable RXDV gate. */
- RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
- DELAY(2000);
+ if (enable) {
+ /* Disable RXDV gate so WOL packets can reach the NIC. */
+ RGE_CLRBIT_1(sc, RGE_PPSW, 0x08);
+ DELAY(2000);
- RGE_SETBIT_1(sc, RGE_CFG1, RGE_CFG1_PM_EN);
- RGE_SETBIT_1(sc, RGE_CFG2, RGE_CFG2_PMSTS_EN);
+ /* Enable power management. */
+ RGE_SETBIT_1(sc, RGE_CFG1, RGE_CFG1_PM_EN);
+ RGE_SETBIT_1(sc, RGE_CFG2, RGE_CFG2_PMSTS_EN);
+ }
}
-#endif
-#endif
diff --git a/sys/dev/rge/if_rge_hw.h b/sys/dev/rge/if_rge_hw.h
index 86f0da7c87b3..4e6ee5f1975f 100644
--- a/sys/dev/rge/if_rge_hw.h
+++ b/sys/dev/rge/if_rge_hw.h
@@ -37,5 +37,6 @@ extern uint16_t rge_read_phy(struct rge_softc *, uint16_t, uint16_t);
extern void rge_write_phy_ocp(struct rge_softc *, uint16_t, uint16_t);
extern uint16_t rge_read_phy_ocp(struct rge_softc *sc, uint16_t reg);
extern int rge_get_link_status(struct rge_softc *);
+extern void rge_wol_config(struct rge_softc *, int);
#endif /* __IF_RGE_HW_H__ */
diff --git a/sys/dev/rge/if_rge_sysctl.c b/sys/dev/rge/if_rge_sysctl.c
index 16001b4c1d94..75e2316042ea 100644
--- a/sys/dev/rge/if_rge_sysctl.c
+++ b/sys/dev/rge/if_rge_sysctl.c
@@ -237,6 +237,11 @@ rge_sysctl_attach(struct rge_softc *sc)
"rx_process_limit", CTLFLAG_RW, &sc->sc_rx_process_limit, 0,
"max number of RX packets to process per interrupt");
+ sc->sc_disable_aspm = 0;
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "disable_aspm", CTLFLAG_RDTUN, &sc->sc_disable_aspm, 0,
+ "disable PCIe ASPM and ECPM (requires reboot)");
+
/* Stats */
rge_sysctl_drv_stats_attach(sc);
rge_sysctl_mac_stats_attach(sc);
diff --git a/sys/dev/rge/if_rgevar.h b/sys/dev/rge/if_rgevar.h
index 89d02e8acb72..2e80dcf42187 100644
--- a/sys/dev/rge/if_rgevar.h
+++ b/sys/dev/rge/if_rgevar.h
@@ -205,6 +205,7 @@ struct rge_softc {
uint32_t sc_debug;
int sc_rx_process_limit;
+ int sc_disable_aspm;
struct rge_drv_stats sc_drv_stats;
diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c
index 30a72652535a..74cefbb50ca1 100644
--- a/sys/dev/thunderbolt/nhi.c
+++ b/sys/dev/thunderbolt/nhi.c
@@ -84,11 +84,6 @@ MALLOC_DEFINE(M_NHI, "nhi", "nhi driver memory");
#define NHI_DEBUG_LEVEL 0
#endif
-/* 0 = default, 1 = force-on, 2 = force-off */
-#ifndef NHI_FORCE_HCM
-#define NHI_FORCE_HCM 0
-#endif
-
void
nhi_get_tunables(struct nhi_softc *sc)
{
@@ -100,7 +95,6 @@ nhi_get_tunables(struct nhi_softc *sc)
/* Set local defaults */
sc->debug = NHI_DEBUG_LEVEL;
sc->max_ring_count = NHI_DEFAULT_NUM_RINGS;
- sc->force_hcm = NHI_FORCE_HCM;
/* Inherit setting from the upstream thunderbolt switch node */
val = TB_GET_DEBUG(sc->dev, &sc->debug);
@@ -128,8 +122,6 @@ nhi_get_tunables(struct nhi_softc *sc)
val = min(val, NHI_MAX_NUM_RINGS);
sc->max_ring_count = max(val, 1);
}
- if (TUNABLE_INT_FETCH("hw.nhi.force_hcm", &val) != 0)
- sc->force_hcm = val;
/* Grab instance variables */
bzero(oid, 80);
@@ -143,24 +135,10 @@ nhi_get_tunables(struct nhi_softc *sc)
val = min(val, NHI_MAX_NUM_RINGS);
sc->max_ring_count = max(val, 1);
}
- snprintf(tmpstr, sizeof(tmpstr), "dev, nhi.%d.force_hcm",
- device_get_unit(sc->dev));
- if (TUNABLE_INT_FETCH(tmpstr, &val) != 0)
- sc->force_hcm = val;
return;
}
-static void
-nhi_configure_caps(struct nhi_softc *sc)
-{
-
- if (NHI_IS_USB4(sc) || (sc->force_hcm == NHI_FORCE_HCM_ON))
- sc->caps |= NHI_CAP_HCM;
- if (sc->force_hcm == NHI_FORCE_HCM_OFF)
- sc->caps &= ~NHI_CAP_HCM;
-}
-
struct nhi_cmd_frame *
nhi_alloc_tx_frame(struct nhi_ring_pair *r)
{
@@ -268,16 +246,14 @@ nhi_attach(struct nhi_softc *sc)
mtx_init(&sc->nhi_mtx, "nhimtx", "NHI Control Mutex", MTX_DEF);
- nhi_configure_caps(sc);
-
/*
* Get the number of TX/RX paths. This sizes some of the register
* arrays during allocation and initialization. USB4 spec says that
- * the max is 21. Alpine Ridge appears to default to 12.
+ * the max is 21.
*/
val = GET_HOST_CAPS_PATHS(nhi_read_reg(sc, NHI_HOST_CAPS));
tb_debug(sc, DBG_INIT|DBG_NOISY, "Total Paths= %d\n", val);
- if ((val == 0) || (val > 21) || ((NHI_IS_AR(sc) && val != 12))) {
+ if (val == 0 || val > 21) {
tb_printf(sc, "WARN: unexpected number of paths: %d\n", val);
/* return (ENXIO); */
}
@@ -297,10 +273,6 @@ nhi_attach(struct nhi_softc *sc)
if (error == 0)
error = tbdev_add_interface(sc);
- if ((error == 0) && (NHI_USE_ICM(sc)))
- tb_printf(sc, "WARN: device uses an internal connection manager\n");
- if ((error == 0) && (NHI_USE_HCM(sc)))
- ;
error = hcm_attach(sc);
if (error == 0)
@@ -312,9 +284,7 @@ nhi_attach(struct nhi_softc *sc)
int
nhi_detach(struct nhi_softc *sc)
{
-
- if (NHI_USE_HCM(sc))
- hcm_detach(sc);
+ hcm_detach(sc);
if (sc->root_rsc != NULL)
tb_router_detach(sc->root_rsc);
@@ -706,16 +676,6 @@ nhi_init(struct nhi_softc *sc)
tb_debug(sc, DBG_INIT, "Setting interrupt auto-ACK, 0x%08x\n", val);
nhi_write_reg(sc, NHI_DMA_MISC, val);
- if (NHI_IS_AR(sc) || NHI_IS_TR(sc) || NHI_IS_ICL(sc))
- tb_printf(sc, "WARN: device uses an internal connection manager\n");
-
- /*
- * Populate the controller (local) UUID, necessary for cross-domain
- * communications.
- if (NHI_IS_ICL(sc))
- nhi_pci_get_uuid(sc);
- */
-
/*
* Attach the router to the root thunderbolt bridge now that the DMA
* channel is configured and ready.
@@ -1163,9 +1123,6 @@ nhi_setup_sysctl(struct nhi_softc *sc)
SYSCTL_ADD_U16(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"max_rings", CTLFLAG_RD, &sc->max_ring_count, 0,
"Max number of rings available");
- SYSCTL_ADD_U8(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
- "force_hcm", CTLFLAG_RD, &sc->force_hcm, 0,
- "Force on/off the function of the host connection manager");
return (0);
}
diff --git a/sys/dev/thunderbolt/nhi_pci.c b/sys/dev/thunderbolt/nhi_pci.c
index 14cae7427448..777f8dd5352e 100644
--- a/sys/dev/thunderbolt/nhi_pci.c
+++ b/sys/dev/thunderbolt/nhi_pci.c
@@ -68,7 +68,6 @@ static int nhi_pci_resume(device_t);
static void nhi_pci_free(struct nhi_softc *);
static int nhi_pci_allocate_interrupts(struct nhi_softc *);
static void nhi_pci_free_resources(struct nhi_softc *);
-static int nhi_pci_icl_poweron(struct nhi_softc *);
static device_method_t nhi_methods[] = {
DEVMETHOD(device_probe, nhi_pci_probe),
@@ -89,67 +88,18 @@ static driver_t nhi_pci_driver = {
sizeof(struct nhi_softc)
};
-struct nhi_ident {
- uint16_t vendor;
- uint16_t device;
- uint16_t subvendor;
- uint16_t subdevice;
- uint32_t flags;
- const char *desc;
-} nhi_identifiers[] = {
- { VENDOR_INTEL, DEVICE_AR_2C_NHI, 0xffff, 0xffff, NHI_TYPE_AR,
- "Thunderbolt 3 NHI (Alpine Ridge 2C)" },
- { VENDOR_INTEL, DEVICE_AR_DP_B_NHI, 0xffff, 0xffff, NHI_TYPE_AR,
- "Thunderbolt 3 NHI (Alpine Ridge 4C Rev B)" },
- { VENDOR_INTEL, DEVICE_AR_DP_C_NHI, 0xffff, 0xffff, NHI_TYPE_AR,
- "Thunderbolt 3 NHI (Alpine Ridge 4C Rev C)" },
- { VENDOR_INTEL, DEVICE_AR_LP_NHI, 0xffff, 0xffff, NHI_TYPE_AR,
- "Thunderbolt 3 NHI (Alpine Ridge LP 2C)" },
- { VENDOR_INTEL, DEVICE_ICL_NHI_0, 0xffff, 0xffff, NHI_TYPE_ICL,
- "Thunderbolt 3 NHI Port 0 (IceLake)" },
- { VENDOR_INTEL, DEVICE_ICL_NHI_1, 0xffff, 0xffff, NHI_TYPE_ICL,
- "Thunderbolt 3 NHI Port 1 (IceLake)" },
- { VENDOR_AMD, DEVICE_PINK_SARDINE_0, 0xffff, 0xffff, NHI_TYPE_USB4,
- "USB4 NHI Port 0 (Pink Sardine)" },
- { VENDOR_AMD, DEVICE_PINK_SARDINE_1, 0xffff, 0xffff, NHI_TYPE_USB4,
- "USB4 NHI Port 1 (Pink Sardine)" },
- { 0, 0, 0, 0, 0, NULL }
-};
-
DRIVER_MODULE_ORDERED(nhi, pci, nhi_pci_driver, NULL, NULL,
SI_ORDER_ANY);
-static struct nhi_ident *
-nhi_find_ident(device_t dev)
-{
- struct nhi_ident *n;
-
- for (n = nhi_identifiers; n->vendor != 0; n++) {
- if (n->vendor != pci_get_vendor(dev))
- continue;
- if (n->device != pci_get_device(dev))
- continue;
- if ((n->subvendor != 0xffff) &&
- (n->subvendor != pci_get_subvendor(dev)))
- continue;
- if ((n->subdevice != 0xffff) &&
- (n->subdevice != pci_get_subdevice(dev)))
- continue;
- return (n);
- }
-
- return (NULL);
-}
-
static int
nhi_pci_probe(device_t dev)
{
- struct nhi_ident *n;
-
if (resource_disabled("tb", 0))
return (ENXIO);
- if ((n = nhi_find_ident(dev)) != NULL) {
- device_set_desc(dev, n->desc);
+ if ((pci_get_class(dev) == PCIC_SERIALBUS)
+ && (pci_get_subclass(dev) == PCIS_SERIALBUS_USB)
+ && (pci_get_progif(dev) == PCIP_SERIALBUS_USB_USB4)) {
+ device_set_desc(dev, "Generic USB4 NHI");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
@@ -161,14 +111,12 @@ nhi_pci_attach(device_t dev)
devclass_t dc;
bus_dma_template_t t;
struct nhi_softc *sc;
- struct nhi_ident *n;
int error = 0;
sc = device_get_softc(dev);
bzero(sc, sizeof(*sc));
sc->dev = dev;
- n = nhi_find_ident(dev);
- sc->hwflags = n->flags;
+ sc->hwflags = NHI_TYPE_USB4;
nhi_get_tunables(sc);
tb_debug(sc, DBG_INIT|DBG_FULL, "busmaster status was %s\n",
@@ -188,12 +136,6 @@ nhi_pci_attach(device_t dev)
tb_printf(sc, "Upstream Facing Port is %s\n",
device_get_nameunit(sc->ufp));
- if (NHI_IS_ICL(sc)) {
- if ((error = nhi_pci_icl_poweron(sc)) != 0)
- return (error);
- }
-
-
/* Allocate BAR0 DMA registers */
sc->regs_rid = PCIR_BAR(0);
if ((sc->regs_resource = bus_alloc_resource_any(dev,
@@ -476,56 +418,3 @@ nhi_pci_disable_interrupts(struct nhi_softc *sc)
nhi_read_reg(sc, NHI_ISR0);
nhi_read_reg(sc, NHI_ISR1);
}
-
-/*
- * Icelake controllers need to be notified of power-on
- */
-static int
-nhi_pci_icl_poweron(struct nhi_softc *sc)
-{
- device_t dev;
- uint32_t val;
- int i, error = 0;
-
- dev = sc->dev;
- val = pci_read_config(dev, ICL_VSCAP_9, 4);
- tb_debug(sc, DBG_INIT, "icl_poweron val= 0x%x\n", val);
- if (val & ICL_VSCAP9_FWREADY)
- return (0);
-
- val = pci_read_config(dev, ICL_VSCAP_22, 4);
- val |= ICL_VSCAP22_FORCEPWR;
- tb_debug(sc, DBG_INIT|DBG_FULL, "icl_poweron writing 0x%x\n", val);
- pci_write_config(dev, ICL_VSCAP_22, val, 4);
-
- error = ETIMEDOUT;
- for (i = 0; i < 15; i++) {
- DELAY(1000000);
- val = pci_read_config(dev, ICL_VSCAP_9, 4);
- if (val & ICL_VSCAP9_FWREADY) {
- error = 0;
- break;
- }
- }
-
- return (error);
-}
-
-/*
- * Icelake and Alderlake controllers store their UUID in PCI config space
- */
-int
-nhi_pci_get_uuid(struct nhi_softc *sc)
-{
- device_t dev;
- uint32_t val[4];
-
- dev = sc->dev;
- val[0] = pci_read_config(dev, ICL_VSCAP_10, 4);
- val[1] = pci_read_config(dev, ICL_VSCAP_11, 4);
- val[2] = 0xffffffff;
- val[3] = 0xffffffff;
-
- bcopy(val, &sc->uuid, 16);
- return (0);
-}
diff --git a/sys/dev/thunderbolt/nhi_var.h b/sys/dev/thunderbolt/nhi_var.h
index e79ecc954c1f..e22c0f4a2bae 100644
--- a/sys/dev/thunderbolt/nhi_var.h
+++ b/sys/dev/thunderbolt/nhi_var.h
@@ -142,19 +142,9 @@ struct nhi_softc {
u_int debug;
u_int hwflags;
#define NHI_TYPE_UNKNOWN 0x00
-#define NHI_TYPE_AR 0x01 /* Alpine Ridge */
-#define NHI_TYPE_TR 0x02 /* Titan Ridge */
-#define NHI_TYPE_ICL 0x03 /* IceLake */
-#define NHI_TYPE_MR 0x04 /* Maple Ridge */
-#define NHI_TYPE_ADL 0x05 /* AlderLake */
#define NHI_TYPE_USB4 0x0f
#define NHI_TYPE_MASK 0x0f
#define NHI_MBOX_BUSY 0x10
- u_int caps;
-#define NHI_CAP_ICM 0x01
-#define NHI_CAP_HCM 0x02
-#define NHI_USE_ICM(sc) ((sc)->caps & NHI_CAP_ICM)
-#define NHI_USE_HCM(sc) ((sc)->caps & NHI_CAP_HCM)
struct hcm_softc *hcm;
struct router_softc *root_rsc;
@@ -194,11 +184,6 @@ struct nhi_softc {
struct intr_config_hook ich;
- uint8_t force_hcm;
-#define NHI_FORCE_HCM_DEFAULT 0x00
-#define NHI_FORCE_HCM_ON 0x01
-#define NHI_FORCE_HCM_OFF 0x02
-
uint8_t uuid[16];
uint8_t lc_uuid[16];
};
@@ -209,9 +194,6 @@ struct nhi_dispatch {
void *context;
};
-#define NHI_IS_AR(sc) (((sc)->hwflags & NHI_TYPE_MASK) == NHI_TYPE_AR)
-#define NHI_IS_TR(sc) (((sc)->hwflags & NHI_TYPE_MASK) == NHI_TYPE_TR)
-#define NHI_IS_ICL(sc) (((sc)->hwflags & NHI_TYPE_MASK) == NHI_TYPE_ICL)
#define NHI_IS_USB4(sc) (((sc)->hwflags & NHI_TYPE_MASK) == NHI_TYPE_USB4)
int nhi_pci_configure_interrupts(struct nhi_softc *sc);
diff --git a/sys/dev/thunderbolt/tb_pcib.c b/sys/dev/thunderbolt/tb_pcib.c
index ffb85ebec9ae..b30de5a7493c 100644
--- a/sys/dev/thunderbolt/tb_pcib.c
+++ b/sys/dev/thunderbolt/tb_pcib.c
@@ -90,18 +90,6 @@ struct tb_pcib_ident {
uint32_t flags; /* This follows the tb_softc flags */
const char *desc;
} tb_pcib_identifiers[] = {
- { VENDOR_INTEL, TB_DEV_AR_2C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
- "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge 2C)" },
- { VENDOR_INTEL, TB_DEV_AR_LP, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
- "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge LP)" },
- { VENDOR_INTEL, TB_DEV_AR_C_4C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
- "Thunderbolt 3 PCI-PCI Bridge (Alpine Ridge C 4C)" },
- { VENDOR_INTEL, TB_DEV_AR_C_2C, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_AR,
- "Thunderbolt 3 PCI-PCI Bridge C (Alpine Ridge C 2C)" },
- { VENDOR_INTEL, TB_DEV_ICL_0, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_ICL,
- "Thunderbolt 3 PCI-PCI Bridge (IceLake)" },
- { VENDOR_INTEL, TB_DEV_ICL_1, 0xffff, 0xffff, TB_GEN_TB3|TB_HWIF_ICL,
- "Thunderbolt 3 PCI-PCI Bridge (IceLake)" },
{ 0, 0, 0, 0, 0, NULL }
};
diff --git a/sys/dev/uart/uart_bus_pci.c b/sys/dev/uart/uart_bus_pci.c
index b0d285e3c603..97c5ff84d251 100644
--- a/sys/dev/uart/uart_bus_pci.c
+++ b/sys/dev/uart/uart_bus_pci.c
@@ -280,33 +280,43 @@ uart_pci_probe(device_t dev)
{
struct uart_softc *sc;
const struct pci_id *id;
- struct pci_id cid = {
- .regshft = 0,
- .rclk = 0,
- .rid = 0x10 | PCI_NO_MSI,
- .desc = "Generic SimpleComm PCI device",
- };
- int result;
sc = device_get_softc(dev);
id = uart_pci_match(dev, pci_ns8250_ids);
if (id != NULL) {
sc->sc_class = &uart_ns8250_class;
- goto match;
+ return (BUS_PROBE_SPECIFIC);
}
if (pci_get_class(dev) == PCIC_SIMPLECOMM &&
pci_get_subclass(dev) == PCIS_SIMPLECOMM_UART &&
pci_get_progif(dev) <= PCIP_SIMPLECOMM_UART_16550A) {
- /* XXX rclk what to do */
- id = &cid;
sc->sc_class = &uart_ns8250_class;
- goto match;
+ return (BUS_PROBE_GENERIC);
}
/* Add checks for non-ns8250 IDs here. */
return (ENXIO);
+}
+
+static int
+uart_pci_attach(device_t dev)
+{
+ static const struct pci_id cid = {
+ .regshft = 0,
+ .rclk = 0,
+ .rid = 0x10 | PCI_NO_MSI,
+ .desc = "Generic SimpleComm PCI device",
+ };
+ struct uart_softc *sc;
+ const struct pci_id *id = uart_pci_match(dev, pci_ns8250_ids);
+ int count, result;
+
+ if (id == NULL)
+ /* No specific PCI ID match, must be a generic device. */
+ id = &cid;
+
+ sc = device_get_softc(dev);
- match:
result = uart_bus_probe(dev, id->regshft, 0, id->rclk,
id->rid & PCI_RID_MASK, 0, 0);
/* Bail out on error. */
@@ -322,26 +332,13 @@ uart_pci_probe(device_t dev)
/* Set/override the device description. */
if (id->desc)
device_set_desc(dev, id->desc);
- return (result);
-}
-
-static int
-uart_pci_attach(device_t dev)
-{
- struct uart_softc *sc;
- const struct pci_id *id;
- int count;
-
- sc = device_get_softc(dev);
/*
- * Use MSI in preference to legacy IRQ if available. However, experience
- * suggests this is only reliable when one MSI vector is advertised.
+ * Use MSI in preference to legacy IRQ if available. However,
+ * experience suggests this is only reliable when one MSI vector is
+ * advertised.
*/
- id = uart_pci_match(dev, pci_ns8250_ids);
- /* Always disable MSI for generic devices. */
- if (id != NULL && (id->rid & PCI_NO_MSI) == 0 &&
- pci_msi_count(dev) == 1) {
+ if ((id->rid & PCI_NO_MSI) == 0 && pci_msi_count(dev) == 1) {
count = 1;
if (pci_alloc_msi(dev, &count) == 0) {
sc->sc_irid = 1;
diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c
index b0c7cd4b44e1..c13eabe9055e 100644
--- a/sys/dev/uart/uart_dev_ns8250.c
+++ b/sys/dev/uart/uart_dev_ns8250.c
@@ -999,6 +999,15 @@ ns8250_bus_probe(struct uart_softc *sc)
uart_setreg(bas, REG_IER, ier);
uart_setreg(bas, REG_MCR, mcr);
uart_setreg(bas, REG_FCR, 0);
+ /*
+ * The Alder Lake AMT SOL Redirection device will never
+ * set LSR_OE (when in loopback mode at least) and
+ * instead block further input by not setting LSR_TEMT.
+ * Recovering the device afterwards into a working
+ * state requires re-writing the LCR register. This
+ * should be harmless on all other devices.
+ */
+ uart_setreg(bas, REG_LCR, uart_getreg(bas, REG_LCR));
uart_barrier(bas);
count = 0;
goto describe;
diff --git a/sys/dev/usb/input/wsp.c b/sys/dev/usb/input/wsp.c
index f78d64f69c08..2d7e3b796b17 100644
--- a/sys/dev/usb/input/wsp.c
+++ b/sys/dev/usb/input/wsp.c
@@ -231,6 +231,7 @@ enum tp_type {
/* list of device capability bits */
#define HAS_INTEGRATED_BUTTON 1
+#define SUPPORTS_FORCETOUCH 2
/* trackpad finger data block size */
#define FSIZE_TYPE1 (14 * 2)
@@ -285,7 +286,7 @@ struct wsp_tp {
.delta = 0,
},
[TYPE4] = {
- .caps = HAS_INTEGRATED_BUTTON,
+ .caps = HAS_INTEGRATED_BUTTON | SUPPORTS_FORCETOUCH,
.button = BUTTON_TYPE4,
.offset = FINGER_TYPE4,
.fsize = FSIZE_TYPE4,
@@ -896,7 +897,8 @@ wsp_attach(device_t dev)
WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
/* finger pressure */
- WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
+ if ((sc->sc_params->tp->caps & SUPPORTS_FORCETOUCH) != 0)
+ WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
/* finger major/minor axis */
WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
@@ -1066,6 +1068,10 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error)
if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) {
evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt);
evdev_sync(sc->sc_evdev);
+ if ((sc->sc_fflags & FREAD) == 0 ||
+ usb_fifo_put_bytes_max(
+ sc->sc_fifo.fp[USB_FIFO_RX]) == 0)
+ goto tr_setup;
}
#endif
sc->sc_status.flags &= ~MOUSE_POSCHANGED;
@@ -1355,7 +1361,12 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
tr_setup:
/* check if we can put more data into the FIFO */
- if (usb_fifo_put_bytes_max(
+ if (
+#ifdef EVDEV_SUPPORT
+ ((evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) != 0 &&
+ (sc->sc_state & WSP_EVDEV_OPENED) != 0) ||
+#endif
+ usb_fifo_put_bytes_max(
sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
usbd_xfer_set_frame_len(xfer, 0,
sc->tp_datalen);
diff --git a/sys/dev/usb/net/if_smsc.c b/sys/dev/usb/net/if_smsc.c
index 0ebbf8482446..8e16b8609144 100644
--- a/sys/dev/usb/net/if_smsc.c
+++ b/sys/dev/usb/net/if_smsc.c
@@ -1585,7 +1585,7 @@ smsc_bootargs_get_mac_addr(device_t dev, struct usb_ether *ue)
node = OF_finddevice("/chosen");
if (node == -1)
return (false);
- if (OF_hasprop(node, "bootargs") == 0) {
+ if (!OF_hasprop(node, "bootargs")) {
smsc_dbg_printf((struct smsc_softc *)ue->ue_sc,
"bootargs not found");
return (false);
diff --git a/sys/dev/usb/serial/uvscom.c b/sys/dev/usb/serial/uvscom.c
index b9add5c1b37b..c5086f7e86cf 100644
--- a/sys/dev/usb/serial/uvscom.c
+++ b/sys/dev/usb/serial/uvscom.c
@@ -551,8 +551,9 @@ uvscom_pre_param(struct ucom_softc *ucom, struct termios *t)
case B38400:
case B57600:
case B115200:
+ break;
default:
- return (EINVAL);
+ return (EINVAL);
}
return (0);
}
diff --git a/sys/dev/virtio/block/virtio_blk.c b/sys/dev/virtio/block/virtio_blk.c
index 4cedb9b45a0e..96846eb0529a 100644
--- a/sys/dev/virtio/block/virtio_blk.c
+++ b/sys/dev/virtio/block/virtio_blk.c
@@ -931,10 +931,57 @@ vtblk_hdr_load_callback(void *arg, bus_dma_segment_t *segs, int nsegs,
}
static int
+vtblk_create_request(struct vtblk_softc *sc, struct vtblk_request *req)
+{
+ req->vbr_sc = sc;
+
+ if (bus_dmamap_create(sc->vtblk_dmat, 0, &req->vbr_mapp))
+ goto error_free;
+
+ if (bus_dmamem_alloc(sc->vtblk_hdr_dmat, (void **)&req->vbr_hdr,
+ BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+ &req->vbr_hdr_mapp))
+ goto error_destroy;
+
+ if (bus_dmamem_alloc(sc->vtblk_ack_dmat, (void **)&req->vbr_ack,
+ BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+ &req->vbr_ack_mapp))
+ goto error_hdr_free;
+
+ MPASS(sglist_count(req->vbr_hdr, sizeof(*req->vbr_hdr)) == 1);
+ MPASS(sglist_count(req->vbr_ack, sizeof(*req->vbr_ack)) == 1);
+
+ if (bus_dmamap_load(sc->vtblk_hdr_dmat, req->vbr_hdr_mapp,
+ req->vbr_hdr, sizeof(struct virtio_blk_outhdr),
+ vtblk_hdr_load_callback, req, BUS_DMA_NOWAIT))
+ goto error_ack_free;
+
+ if (bus_dmamap_load(sc->vtblk_ack_dmat, req->vbr_ack_mapp,
+ req->vbr_ack, sizeof(uint8_t), vtblk_ack_load_callback,
+ req, BUS_DMA_NOWAIT))
+ goto error_hdr_unload;
+
+ return (0);
+
+error_hdr_unload:
+ bus_dmamap_unload(sc->vtblk_hdr_dmat, req->vbr_hdr_mapp);
+error_ack_free:
+ bus_dmamem_free(sc->vtblk_ack_dmat, req->vbr_ack, req->vbr_ack_mapp);
+error_hdr_free:
+ bus_dmamem_free(sc->vtblk_hdr_dmat, req->vbr_hdr, req->vbr_hdr_mapp);
+error_destroy:
+ bus_dmamap_destroy(sc->vtblk_dmat, req->vbr_mapp);
+error_free:
+
+ return (ENOMEM);
+}
+
+static int
vtblk_request_prealloc(struct vtblk_softc *sc)
{
struct vtblk_request *req;
int i, nreqs;
+ int error;
nreqs = virtqueue_size(sc->vtblk_vq);
@@ -951,52 +998,19 @@ vtblk_request_prealloc(struct vtblk_softc *sc)
if (req == NULL)
return (ENOMEM);
- req->vbr_sc = sc;
-
- if (bus_dmamap_create(sc->vtblk_dmat, 0, &req->vbr_mapp))
- goto error_free;
-
- if (bus_dmamem_alloc(sc->vtblk_hdr_dmat, (void **)&req->vbr_hdr,
- BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
- &req->vbr_hdr_mapp))
- goto error_destroy;
-
- if (bus_dmamem_alloc(sc->vtblk_ack_dmat, (void **)&req->vbr_ack,
- BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
- &req->vbr_ack_mapp))
- goto error_hdr_free;
-
- MPASS(sglist_count(req->vbr_hdr, sizeof(*req->vbr_hdr)) == 1);
- MPASS(sglist_count(req->vbr_ack, sizeof(*req->vbr_ack)) == 1);
-
- if (bus_dmamap_load(sc->vtblk_hdr_dmat, req->vbr_hdr_mapp,
- req->vbr_hdr, sizeof(struct virtio_blk_outhdr),
- vtblk_hdr_load_callback, req, BUS_DMA_NOWAIT))
- goto error_ack_free;
-
- if (bus_dmamap_load(sc->vtblk_ack_dmat, req->vbr_ack_mapp,
- req->vbr_ack, sizeof(uint8_t), vtblk_ack_load_callback,
- req, BUS_DMA_NOWAIT))
- goto error_hdr_unload;
+ error = vtblk_create_request(sc, req);
+ if (error) {
+ free(req, M_DEVBUF);
+ return (error);
+ }
sc->vtblk_request_count++;
vtblk_request_enqueue(sc, req);
}
- return (0);
+ error = vtblk_create_request(sc, &sc->vtblk_dump_request);
-error_hdr_unload:
- bus_dmamap_unload(sc->vtblk_hdr_dmat, req->vbr_hdr_mapp);
-error_ack_free:
- bus_dmamem_free(sc->vtblk_ack_dmat, req->vbr_ack, req->vbr_ack_mapp);
-error_hdr_free:
- bus_dmamem_free(sc->vtblk_hdr_dmat, req->vbr_hdr, req->vbr_hdr_mapp);
-error_destroy:
- bus_dmamap_destroy(sc->vtblk_dmat, req->vbr_mapp);
-error_free:
- free(req, M_DEVBUF);
-
- return (ENOMEM);
+ return (error);
}
static void
diff --git a/sys/geom/label/g_label.c b/sys/geom/label/g_label.c
index faefbd7c2ef6..85d55444f91a 100644
--- a/sys/geom/label/g_label.c
+++ b/sys/geom/label/g_label.c
@@ -251,8 +251,8 @@ g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp,
if ((pp2->flags & G_PF_ORPHAN) != 0)
continue;
if (strcmp(pp2->name, name) == 0) {
- G_LABEL_DEBUG(1, "Label %s(%s) already exists (%s).",
- label, name, pp->name);
+ G_LABEL_DEBUG(1, "Label %s (%s) already exists.",
+ label, name);
if (req != NULL) {
gctl_error(req, "Provider %s already exists.",
name);
diff --git a/sys/i386/i386/bioscall.S b/sys/i386/i386/bioscall.S
index d4b13a7012fa..304707182267 100644
--- a/sys/i386/i386/bioscall.S
+++ b/sys/i386/i386/bioscall.S
@@ -156,3 +156,5 @@ CNAME(bios16_jmp):
pushl %ecx /* return address */
movl $KCSEL,4(%esp)
lret /* reload %cs on the way out */
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/exception.S b/sys/i386/i386/exception.S
index a2a59db3c836..394f59f4f82b 100644
--- a/sys/i386/i386/exception.S
+++ b/sys/i386/i386/exception.S
@@ -682,3 +682,5 @@ outofnmi:
#endif
ENTRY(end_exceptions)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/locore.S b/sys/i386/i386/locore.S
index dd9bdfc6c532..e51f1a237d9e 100644
--- a/sys/i386/i386/locore.S
+++ b/sys/i386/i386/locore.S
@@ -449,3 +449,5 @@ END(identify_cpu)
ENTRY(hypercall_page)
.skip 0x1000, 0x90 /* Fill with "nop"s */
#endif
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/mpboot.S b/sys/i386/i386/mpboot.S
index a5367d57a88e..2ab3a6bde034 100644
--- a/sys/i386/i386/mpboot.S
+++ b/sys/i386/i386/mpboot.S
@@ -279,3 +279,5 @@ BOOTMP2:
.globl bootMP_size
bootMP_size:
.long BOOTMP2 - BOOTMP1
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/sigtramp.S b/sys/i386/i386/sigtramp.S
index 11e13d3ded60..ad4157279fd1 100644
--- a/sys/i386/i386/sigtramp.S
+++ b/sys/i386/i386/sigtramp.S
@@ -133,3 +133,5 @@ szosigcode:
sz_lcall_tramp:
.long esigcode-lcall_tramp
#endif
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/support.S b/sys/i386/i386/support.S
index 982108a0b968..e5e39c9ec5d4 100644
--- a/sys/i386/i386/support.S
+++ b/sys/i386/i386/support.S
@@ -586,3 +586,5 @@ ENTRY(cpu_sync_core)
pushl %eax
iretl
END(cpu_sync_core)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/i386/i386/swtch.S b/sys/i386/i386/swtch.S
index ad7c1d16c788..a82bcefd494e 100644
--- a/sys/i386/i386/swtch.S
+++ b/sys/i386/i386/swtch.S
@@ -453,3 +453,5 @@ ENTRY(resumectx)
xorl %eax,%eax
lret
END(resumectx)
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 2fa0621bdfca..48303926759b 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -2006,6 +2006,21 @@ fdgrowtable(struct filedesc *fdp, int nfd)
NDSLOTTYPE *nmap, *omap;
KASSERT(fdp->fd_nfiles > 0, ("zero-length file table"));
+ KASSERT(fdp->fd_nfiles >= NDFILE, ("file table of length %d shorter "
+ "than NDFILE (%d)", fdp->fd_nfiles, NDFILE));
+ KASSERT(fdp->fd_nfiles == NDFILE || fdp->fd_nfiles % NDENTRIES == 0,
+ ("file table of length %d should be multiple of NDENTRIES (%zu)",
+ fdp->fd_nfiles, NDENTRIES));
+ KASSERT((fdp->fd_nfiles == NDFILE) == ((intptr_t)fdp->fd_files -
+ offsetof(struct filedesc0, fd_dfiles) == (intptr_t)fdp -
+ offsetof(struct filedesc0, fd_fd)), ("file table of length %d "
+ "should have %s table", fdp->fd_nfiles, fdp->fd_nfiles == NDFILE ?
+ "initial" : "dynamic"));
+ KASSERT((NDSLOTS(fdp->fd_nfiles) <= NDSLOTS(NDFILE)) == ((intptr_t)
+ fdp->fd_map - offsetof(struct filedesc0, fd_dmap) == (intptr_t)fdp -
+ offsetof(struct filedesc0, fd_fd)), ("file table of length %d "
+ "should have %s map", fdp->fd_nfiles, NDSLOTS(fdp->fd_nfiles) <=
+ NDSLOTS(NDFILE) ? "initial" : "dynamic"));
/* save old values */
onfiles = fdp->fd_nfiles;
@@ -2035,9 +2050,19 @@ fdgrowtable(struct filedesc *fdp, int nfd)
onfiles * sizeof(ntable->fdt_ofiles[0]));
/*
- * Allocate a new map only if the old is not large enough. It will
- * grow at a slower rate than the table as it can map more
- * entries than the table can hold.
+ * Allocate a new map only if the old one is not large enough.
+ *
+ * The initial struct filedesc0 object contains a table and map sized
+ * for NDFILE (20) entries which means the initial map can accomodate
+ * up to NDENTRIES (32 or 64) before requiring reallocation.
+ *
+ * As the new table size (nnfiles) is always rounded up to a multiple
+ * of NDENTRIES, the map will be fully utilised following the first
+ * enlargement, whether it is still the initial map (which will be the
+ * case if nnfiles == NDENTRIES) or if a new one that has has been
+ * allocated (which will be the case if nnfiles == X*NDENTRIES for some
+ * X > 1). In either case, subsequent enlargements will always allocate
+ * a new map to go along with the new table.
*/
if (NDSLOTS(nnfiles) > NDSLOTS(onfiles)) {
nmap = malloc(NDSLOTS(nnfiles) * NDSLOTSIZE, M_FILEDESC,
@@ -2045,6 +2070,8 @@ fdgrowtable(struct filedesc *fdp, int nfd)
/* copy over the old data and update the pointer */
memcpy(nmap, omap, NDSLOTS(onfiles) * sizeof(*omap));
fdp->fd_map = nmap;
+ } else {
+ nmap = NULL;
}
/*
@@ -2085,9 +2112,10 @@ fdgrowtable(struct filedesc *fdp, int nfd)
/*
* The map does not have the same possibility of threads still
* holding references to it. So always free it as long as it
- * does not reference the original static allocation.
+ * does not reference the original static allocation and a new
+ * map was allocated.
*/
- if (NDSLOTS(onfiles) > NDSLOTS(NDFILE))
+ if (nmap != NULL && NDSLOTS(onfiles) > NDSLOTS(NDFILE))
free(omap, M_FILEDESC);
}
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 01731ca46b6b..8c7a0949f024 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -2953,8 +2953,7 @@ knote_drop_detached(struct knote *kn, struct thread *td)
else
list = &kq->kq_knhash[KN_HASH(kn->kn_id, kq->kq_knhashmask)];
- if (!SLIST_EMPTY(list))
- SLIST_REMOVE(list, kn, knote, kn_link);
+ SLIST_REMOVE(list, kn, knote, kn_link);
if (kn->kn_status & KN_QUEUED)
knote_dequeue(kn);
KQ_UNLOCK_FLUX(kq);
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 384825b7f8ac..bc80adb91cd6 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -4385,6 +4385,7 @@ prison_priv_check(struct ucred *cred, int priv)
case PRIV_NET_SETIFVNET:
case PRIV_NET_SETIFFIB:
case PRIV_NET_OVPN:
+ case PRIV_NET_GENEVE:
case PRIV_NET_ME:
case PRIV_NET_WG:
diff --git a/sys/kern/subr_early.c b/sys/kern/subr_early.c
index 62d271f56d75..ae06e4b4cbd2 100644
--- a/sys/kern/subr_early.c
+++ b/sys/kern/subr_early.c
@@ -26,8 +26,6 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
-#include <sys/types.h>
#include <sys/systm.h>
#include <machine/cpu.h>
diff --git a/sys/kern/subr_hash.c b/sys/kern/subr_hash.c
index 23bb205909b1..e74ad825966e 100644
--- a/sys/kern/subr_hash.c
+++ b/sys/kern/subr_hash.c
@@ -1,6 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
+ * Copyright (c) 2026 Gleb Smirnoff <glebius@FreeBSD.org>
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
@@ -37,6 +38,329 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
+#include <sys/ck.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/rmlock.h>
+#include <sys/rwlock.h>
+#include <sys/sx.h>
+#include <sys/hash.h>
+
+#define ASSERT_NOPAD(head, lock) _Static_assert( \
+ sizeof(head ## _HEAD(, foo)) + sizeof(struct lock) == \
+ sizeof(struct { head ## _HEAD(, foo) h; struct lock l; }), \
+ "Structure of " #head "_HEAD and " #lock " has padding")
+ASSERT_NOPAD(LIST, mtx);
+ASSERT_NOPAD(CK_LIST, mtx);
+ASSERT_NOPAD(SLIST, mtx);
+ASSERT_NOPAD(CK_SLIST, mtx);
+ASSERT_NOPAD(STAILQ, mtx);
+ASSERT_NOPAD(CK_STAILQ, mtx);
+ASSERT_NOPAD(TAILQ, mtx);
+ASSERT_NOPAD(LIST, rwlock);
+ASSERT_NOPAD(CK_LIST, rwlock);
+ASSERT_NOPAD(SLIST, rwlock);
+ASSERT_NOPAD(CK_SLIST, rwlock);
+ASSERT_NOPAD(STAILQ, rwlock);
+ASSERT_NOPAD(CK_STAILQ, rwlock);
+ASSERT_NOPAD(TAILQ, rwlock);
+ASSERT_NOPAD(LIST, sx);
+ASSERT_NOPAD(CK_LIST, sx);
+ASSERT_NOPAD(SLIST, sx);
+ASSERT_NOPAD(CK_SLIST, sx);
+ASSERT_NOPAD(STAILQ, sx);
+ASSERT_NOPAD(CK_STAILQ, sx);
+ASSERT_NOPAD(TAILQ, sx);
+ASSERT_NOPAD(LIST, rmlock);
+ASSERT_NOPAD(CK_LIST, rmlock);
+ASSERT_NOPAD(SLIST, rmlock);
+ASSERT_NOPAD(CK_SLIST, rmlock);
+ASSERT_NOPAD(STAILQ, rmlock);
+ASSERT_NOPAD(CK_STAILQ, rmlock);
+ASSERT_NOPAD(TAILQ, rmlock);
+ASSERT_NOPAD(LIST, rmslock);
+ASSERT_NOPAD(CK_LIST, rmslock);
+ASSERT_NOPAD(SLIST, rmslock);
+ASSERT_NOPAD(CK_SLIST, rmslock);
+ASSERT_NOPAD(STAILQ, rmslock);
+ASSERT_NOPAD(CK_STAILQ, rmslock);
+ASSERT_NOPAD(TAILQ, rmslock);
+#undef ASSERT_NOPAD
+
+static inline void
+hashalloc_sizes(struct hashalloc_args *args, size_t *hdrsize, size_t *loffset)
+{
+ switch (args->head) {
+ case HASH_HEAD_LIST:
+ *loffset = sizeof(LIST_HEAD(, foo));
+ break;
+ case HASH_HEAD_CK_LIST:
+ *loffset = sizeof(CK_LIST_HEAD(, foo));
+ break;
+ case HASH_HEAD_SLIST:
+ *loffset = sizeof(SLIST_HEAD(, foo));
+ break;
+ case HASH_HEAD_CK_SLIST:
+ *loffset = sizeof(CK_SLIST_HEAD(, foo));
+ break;
+ case HASH_HEAD_STAILQ:
+ *loffset = sizeof(STAILQ_HEAD(, foo));
+ break;
+ case HASH_HEAD_CK_STAILQ:
+ *loffset = sizeof(CK_STAILQ_HEAD(, foo));
+ break;
+ case HASH_HEAD_TAILQ:
+ *loffset = sizeof(TAILQ_HEAD(, foo));
+ break;
+ }
+
+ switch (args->lock) {
+ case HASH_LOCK_NONE:
+ *hdrsize = *loffset;
+ break;
+ case HASH_LOCK_MTX:
+ *hdrsize = *loffset + sizeof(struct mtx);
+ break;
+ case HASH_LOCK_RWLOCK:
+ *hdrsize = *loffset + sizeof(struct rwlock);
+ break;
+ case HASH_LOCK_SX:
+ *hdrsize = *loffset + sizeof(struct sx);
+ break;
+ case HASH_LOCK_RMLOCK:
+ *hdrsize = *loffset + sizeof(struct rmlock);
+ break;
+ case HASH_LOCK_RMSLOCK:
+ *hdrsize = *loffset + sizeof(struct rmslock);
+ break;
+ }
+
+ if (args->hdrsize > 0) {
+ MPASS(args->hdrsize >= *hdrsize);
+ *hdrsize = args->hdrsize;
+ } else
+ args->hdrsize = *hdrsize;
+}
+
+void *
+hashalloc(struct hashalloc_args *args)
+{
+ static const int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021,
+ 1531, 2039, 2557, 3067, 3583, 4093, 4603, 5119, 5623, 6143, 6653,
+ 7159, 7673, 8191, 12281, 16381, 24571, 32749 };
+ void *mem;
+ size_t size, hdrsize, loffset;
+ u_int i;
+
+ MPASS(args->version == 0);
+ MPASS(args->size > 0);
+
+ switch (args->type) {
+ case HASH_TYPE_POWER2:
+ for (size = 1; size <= args->size; size <<= 1)
+ continue;
+ size >>= 1;
+ break;
+ case HASH_TYPE_PRIME:
+ for (i = nitems(primes) - 1; args->size < primes[i]; i--)
+ ;
+ size = primes[i];
+ break;
+ }
+
+ hashalloc_sizes(args, &hdrsize, &loffset);
+
+ mem = malloc(size * hdrsize, args->mtype, args->mflags);
+ if (mem == NULL) {
+ args->error = ENOMEM;
+ return (NULL);
+ }
+
+ switch (args->lock) {
+ case HASH_LOCK_NONE:
+ break;
+ case HASH_LOCK_MTX:
+ MPASS(args->lname != NULL);
+ if ((args->mflags & M_ZERO) == 0)
+ args->lopts |= MTX_NEW;
+ break;
+ case HASH_LOCK_RWLOCK:
+ MPASS(args->lname != NULL);
+ if ((args->mflags & M_ZERO) == 0)
+ args->lopts |= RW_NEW;
+ break;
+ case HASH_LOCK_SX:
+ MPASS(args->lname != NULL);
+ if ((args->mflags & M_ZERO) == 0)
+ args->lopts |= SX_NEW;
+ break;
+ case HASH_LOCK_RMLOCK:
+ MPASS(args->lname != NULL);
+ if ((args->mflags & M_ZERO) == 0)
+ args->lopts |= RM_NEW;
+ break;
+ case HASH_LOCK_RMSLOCK:
+ MPASS(args->lname != NULL);
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ void *slot;
+
+ slot = (char *)mem + i * hdrsize;
+ switch (args->head) {
+ case HASH_HEAD_LIST:
+ LIST_INIT((LIST_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_CK_LIST:
+ CK_LIST_INIT((CK_LIST_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_SLIST:
+ SLIST_INIT((SLIST_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_CK_SLIST:
+ CK_SLIST_INIT((CK_SLIST_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_STAILQ:
+ STAILQ_INIT((STAILQ_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_CK_STAILQ:
+ CK_STAILQ_INIT((CK_STAILQ_HEAD(, foo) *)slot);
+ break;
+ case HASH_HEAD_TAILQ:
+ TAILQ_INIT((TAILQ_HEAD(, foo) *)slot);
+ break;
+ }
+
+ slot = (char *)slot + loffset;
+ switch (args->lock) {
+ case HASH_LOCK_NONE:
+ break;
+ case HASH_LOCK_MTX:
+ mtx_init((struct mtx *)slot, args->lname, NULL,
+ args->lopts);
+ break;
+ case HASH_LOCK_RWLOCK:
+ rw_init_flags((struct rwlock *)slot, args->lname,
+ args->lopts);
+ break;
+ case HASH_LOCK_SX:
+ sx_init_flags((struct sx *)slot, args->lname,
+ args->lopts);
+ break;
+ case HASH_LOCK_RMLOCK:
+ rm_init_flags((struct rmlock *)slot, args->lname,
+ args->lopts);
+ break;
+ case HASH_LOCK_RMSLOCK:
+ rms_init((struct rmslock *)slot, args->lname);
+ break;
+ }
+
+ if (args->ctor != NULL) {
+ slot = (char *)mem + i * hdrsize;
+ if ((args->error = args->ctor(slot)) != 0) {
+ slot = (char *)slot + loffset;
+ switch (args->lock) {
+ case HASH_LOCK_NONE:
+ break;
+ case HASH_LOCK_MTX:
+ mtx_destroy((struct mtx *)slot);
+ break;
+ case HASH_LOCK_RWLOCK:
+ rw_destroy((struct rwlock *)slot);
+ break;
+ case HASH_LOCK_SX:
+ sx_destroy((struct sx *)slot);
+ break;
+ case HASH_LOCK_RMLOCK:
+ rm_destroy((struct rmlock *)slot);
+ break;
+ case HASH_LOCK_RMSLOCK:
+ rms_destroy((struct rmslock *)slot);
+ break;
+ }
+ args->size = i;
+ hashfree(mem, args);
+ return (NULL);
+ }
+ }
+ }
+
+ args->size = size;
+ return (mem);
+}
+
+void
+hashfree(void *mem, struct hashalloc_args *args)
+{
+ size_t hdrsize, loffset;
+
+ if (__predict_false(mem == NULL))
+ return;
+
+ hashalloc_sizes(args, &hdrsize, &loffset);
+
+ for (u_int i = 0; i < args->size; i++) {
+#ifdef INVARIANTS
+ static const char msg[] =
+ "%s: hashtbl %p not empty (malloc type %s)";
+#endif
+#define HPASS(exp) KASSERT(exp, (msg, __func__, mem, args->mtype->ks_shortdesc))
+ void *slot;
+
+ slot = (char *)mem + i * hdrsize;
+ if (args->dtor != NULL)
+ args->dtor(slot);
+ switch (args->head) {
+ case HASH_HEAD_LIST:
+ HPASS(LIST_EMPTY((LIST_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_CK_LIST:
+ HPASS(CK_LIST_EMPTY((CK_LIST_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_SLIST:
+ HPASS(SLIST_EMPTY((SLIST_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_CK_SLIST:
+ HPASS(CK_SLIST_EMPTY((CK_SLIST_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_STAILQ:
+ HPASS(STAILQ_EMPTY((STAILQ_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_CK_STAILQ:
+ HPASS(CK_STAILQ_EMPTY((CK_STAILQ_HEAD(, foo) *)slot));
+ break;
+ case HASH_HEAD_TAILQ:
+ HPASS(TAILQ_EMPTY((TAILQ_HEAD(, foo) *)slot));
+ break;
+ }
+#undef HPASS
+
+ slot = (char *)slot + loffset;
+ switch (args->lock) {
+ case HASH_LOCK_NONE:
+ break;
+ case HASH_LOCK_MTX:
+ mtx_destroy((struct mtx *)slot);
+ break;
+ case HASH_LOCK_RWLOCK:
+ rw_destroy((struct rwlock *)slot);
+ break;
+ case HASH_LOCK_SX:
+ sx_destroy((struct sx *)slot);
+ break;
+ case HASH_LOCK_RMLOCK:
+ rm_destroy((struct rmlock *)slot);
+ break;
+ case HASH_LOCK_RMSLOCK:
+ rms_destroy((struct rmslock *)slot);
+ break;
+ }
+ }
+
+ free(mem, args->mtype);
+}
static __inline int
hash_mflags(int flags)
@@ -52,26 +376,17 @@ void *
hashinit_flags(int elements, struct malloc_type *type, u_long *hashmask,
int flags)
{
- long hashsize, i;
- LIST_HEAD(generic, generic) *hashtbl;
-
- KASSERT(elements > 0, ("%s: bad elements", __func__));
- /* Exactly one of HASH_WAITOK and HASH_NOWAIT must be set. */
- KASSERT((flags & HASH_WAITOK) ^ (flags & HASH_NOWAIT),
- ("Bad flags (0x%x) passed to hashinit_flags", flags));
+ struct hashalloc_args args = {
+ .size = elements,
+ .mtype = type,
+ .mflags = hash_mflags(flags),
+ };
+ void *rv;
- for (hashsize = 1; hashsize <= elements; hashsize <<= 1)
- continue;
- hashsize >>= 1;
-
- hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type,
- hash_mflags(flags));
- if (hashtbl != NULL) {
- for (i = 0; i < hashsize; i++)
- LIST_INIT(&hashtbl[i]);
- *hashmask = hashsize - 1;
- }
- return (hashtbl);
+ rv = hashalloc(&args);
+ if (rv != NULL)
+ *hashmask = args.size - 1;
+ return (rv);
}
/*
@@ -87,20 +402,14 @@ hashinit(int elements, struct malloc_type *type, u_long *hashmask)
void
hashdestroy(void *vhashtbl, struct malloc_type *type, u_long hashmask)
{
- LIST_HEAD(generic, generic) *hashtbl, *hp;
+ struct hashalloc_args args = {
+ .size = hashmask + 1,
+ .mtype = type,
+ };
- hashtbl = vhashtbl;
- for (hp = hashtbl; hp <= &hashtbl[hashmask]; hp++)
- KASSERT(LIST_EMPTY(hp), ("%s: hashtbl %p not empty "
- "(malloc type %s)", __func__, hashtbl, type->ks_shortdesc));
- free(hashtbl, type);
+ hashfree(vhashtbl, &args);
}
-static const int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021, 1531,
- 2039, 2557, 3067, 3583, 4093, 4603, 5119, 5623, 6143,
- 6653, 7159, 7673, 8191, 12281, 16381, 24571, 32749 };
-#define NPRIMES nitems(primes)
-
/*
* General routine to allocate a prime number sized hash table with control of
* memory flags.
@@ -108,31 +417,18 @@ static const int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021, 1531,
void *
phashinit_flags(int elements, struct malloc_type *type, u_long *nentries, int flags)
{
- long hashsize, i;
- LIST_HEAD(generic, generic) *hashtbl;
-
- KASSERT(elements > 0, ("%s: bad elements", __func__));
- /* Exactly one of HASH_WAITOK and HASH_NOWAIT must be set. */
- KASSERT((flags & HASH_WAITOK) ^ (flags & HASH_NOWAIT),
- ("Bad flags (0x%x) passed to phashinit_flags", flags));
-
- for (i = 1, hashsize = primes[1]; hashsize <= elements;) {
- i++;
- if (i == NPRIMES)
- break;
- hashsize = primes[i];
- }
- hashsize = primes[i - 1];
-
- hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type,
- hash_mflags(flags));
- if (hashtbl == NULL)
- return (NULL);
+ struct hashalloc_args args = {
+ .size = elements,
+ .mtype = type,
+ .type = HASH_TYPE_PRIME,
+ .mflags = hash_mflags(flags),
+ };
+ void *rv;
- for (i = 0; i < hashsize; i++)
- LIST_INIT(&hashtbl[i]);
- *nentries = hashsize;
- return (hashtbl);
+ rv = hashalloc(&args);
+ if (rv != NULL)
+ *nentries = args.size;
+ return (rv);
}
/*
diff --git a/sys/kern/subr_ticks.S b/sys/kern/subr_ticks.S
index 5cb994293d91..29f44c7b2f78 100644
--- a/sys/kern/subr_ticks.S
+++ b/sys/kern/subr_ticks.S
@@ -40,3 +40,5 @@ ticks =ticksl + TICKS_OFFSET
.type jiffies, %object
jiffies = ticksl
.size jiffies, __SIZEOF_LONG__
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c
index 35009ad77722..4c3a4085b8db 100644
--- a/sys/kern/uipc_ktls.c
+++ b/sys/kern/uipc_ktls.c
@@ -870,21 +870,15 @@ ktls_clone_session(struct ktls_session *tls, int direction)
static int
ktls_try_toe(struct socket *so, struct ktls_session *tls, int direction)
{
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error;
- inp = so->so_pcb;
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- INP_WUNLOCK(inp);
- return (ECONNRESET);
- }
- if (inp->inp_socket == NULL) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
if (!(tp->t_flags & TF_TOE)) {
INP_WUNLOCK(inp);
return (EOPNOTSUPP);
@@ -923,19 +917,14 @@ ktls_alloc_snd_tag(struct inpcb *inp, struct ktls_session *tls, bool force,
union if_snd_tag_alloc_params params;
struct ifnet *ifp;
struct nhop_object *nh;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
int error;
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
return (ECONNRESET);
}
- if (inp->inp_socket == NULL) {
- INP_RUNLOCK(inp);
- return (ECONNRESET);
- }
- tp = intotcpcb(inp);
/*
* Check administrative controls on ifnet TLS to determine if
@@ -1027,11 +1016,7 @@ ktls_alloc_rcv_tag(struct inpcb *inp, struct ktls_session *tls,
return (ENXIO);
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- INP_RUNLOCK(inp);
- return (ECONNRESET);
- }
- if (inp->inp_socket == NULL) {
+ if (intotcpcb(inp)->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
return (ECONNRESET);
}
@@ -1506,23 +1491,15 @@ ktls_get_rx_mode(struct socket *so, int *modep)
int
ktls_get_rx_sequence(struct inpcb *inp, uint32_t *tcpseq, uint64_t *tlsseq)
{
- struct socket *so;
- struct tcpcb *tp;
+ struct socket *so = inp->inp_socket;
+ struct tcpcb *tp = intotcpcb(inp);
INP_RLOCK(inp);
- so = inp->inp_socket;
- if (__predict_false(so == NULL)) {
- INP_RUNLOCK(inp);
- return (EINVAL);
- }
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
- MPASS(tp != NULL);
-
SOCKBUF_LOCK(&so->so_rcv);
*tcpseq = tp->rcv_nxt - so->so_rcv.sb_tlscc;
*tlsseq = so->so_rcv.sb_tls_seqno;
@@ -1697,7 +1674,7 @@ ktls_reset_receive_tag(void *context, int pending)
ifp = NULL;
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (intotcpcb(inp)->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
goto out;
}
@@ -1818,9 +1795,9 @@ ktls_reset_send_tag(void *context, int pending)
} else {
NET_EPOCH_ENTER(et);
INP_WLOCK(inp);
- if (!(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
- CURVNET_SET(inp->inp_vnet);
+ tp = intotcpcb(inp);
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
+ CURVNET_SET(inp->inp_socket->so_vnet);
tp = tcp_drop(tp, ECONNABORTED);
CURVNET_RESTORE();
if (tp != NULL) {
@@ -2461,26 +2438,19 @@ ktls_resync_ifnet(struct socket *so, uint32_t tls_len, uint64_t tls_rcd_num)
{
union if_snd_tag_modify_params params;
struct m_snd_tag *mst;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
mst = so->so_rcv.sb_tls_info->snd_tag;
if (__predict_false(mst == NULL))
return (EINVAL);
- inp = sotoinpcb(so);
- if (__predict_false(inp == NULL))
- return (EINVAL);
-
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
- MPASS(tp != NULL);
-
/* Get the TCP sequence number of the next valid TLS header. */
SOCKBUF_LOCK(&so->so_rcv);
params.tls_rx.tls_hdr_tcp_sn =
@@ -2500,13 +2470,12 @@ ktls_drop(struct socket *so, int error)
{
struct epoch_tracker et;
struct inpcb *inp = sotoinpcb(so);
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
NET_EPOCH_ENTER(et);
INP_WLOCK(inp);
- if (!(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
- CURVNET_SET(inp->inp_vnet);
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
+ CURVNET_SET(inp->inp_socket->so_vnet);
tp = tcp_drop(tp, error);
CURVNET_RESTORE();
if (tp != NULL)
@@ -3372,7 +3341,8 @@ ktls_disable_ifnet_help(void *context, int pending __unused)
INP_WLOCK(inp);
so = inp->inp_socket;
MPASS(so != NULL);
- if (inp->inp_flags & INP_DROPPED) {
+ tp = intotcpcb(inp);
+ if (tp->t_flags & TF_DISCONNECTED) {
goto out;
}
@@ -3383,8 +3353,7 @@ ktls_disable_ifnet_help(void *context, int pending __unused)
if (err == 0) {
counter_u64_add(ktls_ifnet_disable_ok, 1);
/* ktls_set_tx_mode() drops inp wlock, so recheck flags */
- if ((inp->inp_flags & INP_DROPPED) == 0 &&
- (tp = intotcpcb(inp)) != NULL &&
+ if ((tp->t_flags & TF_DISCONNECTED) == 0 &&
tp->t_fb->tfb_hwtls_change != NULL)
(*tp->t_fb->tfb_hwtls_change)(tp, 0);
} else {
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 45290c29f629..3debec547a80 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1768,7 +1768,7 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
return (error);
}
SOCK_SENDBUF_LOCK(so2);
- if (so->so_snd.sb_tls_info != NULL) {
+ if (so2->so_snd.sb_tls_info != NULL) {
SOCK_SENDBUF_UNLOCK(so2);
SOCK_UNLOCK(so2);
mtx_lock(&sp->mtx);
diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c
index 2a790237d30e..da0e36fc1ec5 100644
--- a/sys/kern/vfs_aio.c
+++ b/sys/kern/vfs_aio.c
@@ -2668,8 +2668,7 @@ filt_aiodetach(struct knote *kn)
knl = &kn->kn_ptr.p_aio->klist;
knl->kl_lock(knl->kl_lockarg);
- if (!knlist_empty(knl))
- knlist_remove(knl, kn, 1);
+ knlist_remove(knl, kn, 1);
knl->kl_unlock(knl->kl_lockarg);
}
@@ -2718,8 +2717,7 @@ filt_liodetach(struct knote *kn)
knl = &kn->kn_ptr.p_lio->klist;
knl->kl_lock(knl->kl_lockarg);
- if (!knlist_empty(knl))
- knlist_remove(knl, kn, 1);
+ knlist_remove(knl, kn, 1);
knl->kl_unlock(knl->kl_lockarg);
}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 06500909589e..0c4eeb584d41 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -1168,12 +1168,15 @@ openflags(int *flagsp)
{
int flags;
+ flags = *flagsp;
+ if ((flags & ~FUSERALLOWED) != 0)
+ return (EINVAL);
+
/*
* Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags
* may be specified. On the other hand, for O_PATH any mode
* except O_EXEC is ignored.
*/
- flags = *flagsp;
if ((flags & O_PATH) != 0) {
flags &= ~O_ACCMODE;
} else if ((flags & O_EXEC) != 0) {
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index a4100c31ef26..faedb856977c 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -169,6 +169,7 @@ SUBDIR= \
if_tuntap \
if_vlan \
if_vxlan \
+ if_geneve \
${_if_wg} \
iflib \
${_igc} \
diff --git a/sys/modules/dtb/rockchip/Makefile b/sys/modules/dtb/rockchip/Makefile
index fb29f4f0970a..407d054a144d 100644
--- a/sys/modules/dtb/rockchip/Makefile
+++ b/sys/modules/dtb/rockchip/Makefile
@@ -25,7 +25,8 @@ DTS= \
rockchip/rk3568-bpi-r2-pro.dts \
rockchip/rk3588-rock-5-itx.dts \
rockchip/rk3588-rock-5b.dts \
- rockchip/rk3588-orangepi-5-plus
+ rockchip/rk3588-orangepi-5-plus.dts \
+ rockchip/rk3588-friendlyelec-cm3588-nas.dts
DTSO= rk3328-analog-sound.dtso \
rk3328-i2c0.dtso \
diff --git a/sys/modules/hid/Makefile b/sys/modules/hid/Makefile
index 10720570deb7..a32b9326647e 100644
--- a/sys/modules/hid/Makefile
+++ b/sys/modules/hid/Makefile
@@ -6,6 +6,7 @@ SUBDIR = \
hidraw
SUBDIR += \
+ appleir \
bcm5974 \
hconf \
hcons \
diff --git a/sys/modules/hid/appleir/Makefile b/sys/modules/hid/appleir/Makefile
new file mode 100644
index 000000000000..5b56b7ea3048
--- /dev/null
+++ b/sys/modules/hid/appleir/Makefile
@@ -0,0 +1,8 @@
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= appleir
+SRCS= appleir.c
+SRCS+= opt_hid.h
+SRCS+= bus_if.h device_if.h usbdevs.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/if_geneve/Makefile b/sys/modules/if_geneve/Makefile
new file mode 100644
index 000000000000..1e65d4dbb168
--- /dev/null
+++ b/sys/modules/if_geneve/Makefile
@@ -0,0 +1,7 @@
+.PATH: ${SRCTOP}/sys/net
+
+KMOD= if_geneve
+SRCS= if_geneve.c
+SRCS+= opt_inet.h opt_inet6.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/ix/Makefile b/sys/modules/ix/Makefile
index aec6eaabffdd..2884f6ba898e 100644
--- a/sys/modules/ix/Makefile
+++ b/sys/modules/ix/Makefile
@@ -1,13 +1,15 @@
.PATH: ${SRCTOP}/sys/dev/ixgbe
KMOD = if_ix
-SRCS = device_if.h bus_if.h pci_if.h pci_iov_if.h ifdi_if.h
+SRCS = device_if.h bus_if.h pci_if.h pci_iov_if.h ifdi_if.h mdio_if.h
SRCS += opt_inet.h opt_inet6.h opt_rss.h
SRCS += if_ix.c if_bypass.c if_fdir.c if_sriov.c ix_txrx.c ixgbe_osdep.c
+SRCS += if_ix_mdio.c if_ix_mdio_hw.c
# Shared source
SRCS += ixgbe_common.c ixgbe_api.c ixgbe_phy.c ixgbe_mbx.c ixgbe_vf.c
SRCS += ixgbe_dcb.c ixgbe_dcb_82598.c ixgbe_dcb_82599.c
SRCS += ixgbe_82598.c ixgbe_82599.c ixgbe_x540.c ixgbe_x550.c ixgbe_e610.c
+SRCS += ixgbe_fw_logging.c
CFLAGS+= -I${SRCTOP}/sys/dev/ixgbe
.include <bsd.kmod.mk>
diff --git a/sys/modules/ixv/Makefile b/sys/modules/ixv/Makefile
index e7066bb7829b..743b2b407a5e 100644
--- a/sys/modules/ixv/Makefile
+++ b/sys/modules/ixv/Makefile
@@ -8,6 +8,7 @@ SRCS += if_ixv.c if_fdir.c ix_txrx.c ixgbe_osdep.c
SRCS += ixgbe_common.c ixgbe_api.c ixgbe_phy.c ixgbe_mbx.c ixgbe_vf.c
SRCS += ixgbe_dcb.c ixgbe_dcb_82598.c ixgbe_dcb_82599.c
SRCS += ixgbe_82598.c ixgbe_82599.c ixgbe_x540.c ixgbe_x550.c ixgbe_e610.c
+SRCS += ixgbe_fw_logging.c
CFLAGS+= -I${SRCTOP}/sys/dev/ixgbe
.include <bsd.kmod.mk>
diff --git a/sys/net/bpf.c b/sys/net/bpf.c
index 05877f6410a4..9d0b76753560 100644
--- a/sys/net/bpf.c
+++ b/sys/net/bpf.c
@@ -1201,7 +1201,7 @@ bpfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
case BIOCGDLTLIST32:
case BIOCGRTIMEOUT32:
case BIOCSRTIMEOUT32:
- if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
+ if (SV_CURPROC_FLAG(SV_ILP32)) {
BPFD_LOCK(d);
d->bd_compat32 = 1;
BPFD_UNLOCK(d);
@@ -1209,6 +1209,19 @@ bpfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
}
#endif
+#if defined(COMPAT_FREEBSD32)
+ if (SV_CURPROC_FLAG(SV_ILP32)) {
+ /*
+ * On platforms other than amd64, BIOC[GS]RTIMEOUT32 is equal to
+ * BIOC[GS]RTIMEOUT. Since this is difficult to handle in the
+ * switch command, map them.
+ */
+ if (cmd == BIOCSRTIMEOUT32)
+ cmd = BIOCSRTIMEOUT;
+ if (cmd == BIOCGRTIMEOUT32)
+ cmd = BIOCGRTIMEOUT;
+ }
+#endif
CURVNET_SET(TD_TO_VNET(td));
switch (cmd) {
default:
@@ -1419,23 +1432,19 @@ bpfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
* Set read timeout.
*/
case BIOCSRTIMEOUT:
-#if defined(COMPAT_FREEBSD32) && defined(__amd64__)
- case BIOCSRTIMEOUT32:
-#endif
{
struct timeval *tv = (struct timeval *)addr;
-#if defined(COMPAT_FREEBSD32)
+#ifdef COMPAT_FREEBSD32
struct timeval32 *tv32;
struct timeval tv64;
- if (cmd == BIOCSRTIMEOUT32) {
+ if (SV_CURPROC_FLAG(SV_ILP32)) {
tv32 = (struct timeval32 *)addr;
tv = &tv64;
tv->tv_sec = tv32->tv_sec;
tv->tv_usec = tv32->tv_usec;
- } else
+ }
#endif
- tv = (struct timeval *)addr;
/*
* Subtract 1 tick from tvtohz() since this isn't
@@ -1450,31 +1459,24 @@ bpfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
* Get read timeout.
*/
case BIOCGRTIMEOUT:
-#if defined(COMPAT_FREEBSD32) && defined(__amd64__)
- case BIOCGRTIMEOUT32:
-#endif
{
- struct timeval *tv;
-#if defined(COMPAT_FREEBSD32) && defined(__amd64__)
+ struct timeval *tv = (struct timeval *)addr;
+#ifdef COMPAT_FREEBSD32
struct timeval32 *tv32;
struct timeval tv64;
- if (cmd == BIOCGRTIMEOUT32)
+ if (SV_CURPROC_FLAG(SV_ILP32))
tv = &tv64;
- else
#endif
- tv = (struct timeval *)addr;
-
tv->tv_sec = d->bd_rtout / hz;
tv->tv_usec = (d->bd_rtout % hz) * tick;
-#if defined(COMPAT_FREEBSD32) && defined(__amd64__)
- if (cmd == BIOCGRTIMEOUT32) {
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32)) {
tv32 = (struct timeval32 *)addr;
tv32->tv_sec = tv->tv_sec;
tv32->tv_usec = tv->tv_usec;
}
#endif
-
break;
}
diff --git a/sys/net/cmis.h b/sys/net/cmis.h
new file mode 100644
index 000000000000..00548a20c051
--- /dev/null
+++ b/sys/net/cmis.h
@@ -0,0 +1,450 @@
+/*-
+ * Copyright (c) 2026 Netflix Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * The following set of constants are from the OIF Common Management
+ * Interface Specification (CMIS) revision 5.3, September 2024.
+ *
+ * CMIS defines a 256-byte addressable memory with lower (0-127) and
+ * upper (128-255) regions. Lower memory is always accessible.
+ * Upper memory is paged via byte 127 (page select) and byte 126
+ * (bank select).
+ *
+ * All values are read across an I2C bus at address 0xA0.
+ */
+
+#ifndef _NET_CMIS_H_
+#define _NET_CMIS_H_
+
+#define CMIS_BASE 0xA0 /* Base I2C address for all requests */
+
+/* CMIS Module Types (SFF-8024 Identifier, byte 0) */
+#define CMIS_ID_QSFP_DD 0x18 /* QSFP-DD */
+#define CMIS_ID_QSFP8X 0x19 /* QSFP 8X (OSFP) */
+#define CMIS_ID_SFP_DD 0x1A /* SFP-DD */
+#define CMIS_ID_DSFP 0x1B /* DSFP */
+#define CMIS_ID_QSFP_CMIS 0x1E /* QSFP+ with CMIS */
+
+/* Table 8-4: Lower Memory Map (bytes 0x00-0x7F) */
+enum {
+ /* Table 8-5: Management Characteristics (bytes 0-2) */
+ CMIS_ID = 0, /* SFF-8024 Identifier */
+ CMIS_REV = 1, /* CMIS revision (major.minor) */
+ CMIS_MODULE_TYPE = 2, /* Memory model, config options */
+
+ /* Table 8-6: Global Status (byte 3) */
+ CMIS_MODULE_STATE = 3, /* Module state, interrupt status */
+
+ /* Table 8-8: Flags Summary (bytes 4-7) */
+ CMIS_FLAGS_BANK0 = 4, /* Flags summary, bank 0 */
+ CMIS_FLAGS_BANK1 = 5, /* Flags summary, bank 1 */
+ CMIS_FLAGS_BANK2 = 6, /* Flags summary, bank 2 */
+ CMIS_FLAGS_BANK3 = 7, /* Flags summary, bank 3 */
+
+ /* Table 8-9: Module-Level Flags (bytes 8-13) */
+ CMIS_MOD_FLAGS_START = 8, /* Module firmware/state flags */
+ CMIS_MOD_FLAGS_TEMP_VCC = 9, /* Temp/VCC alarm/warning flags */
+ CMIS_MOD_FLAGS_AUX = 10, /* Aux monitor alarm/warning flags */
+ CMIS_MOD_FLAGS_CUSTOM = 11, /* Custom/Aux3 monitor flags */
+ CMIS_MOD_FLAGS_RSVD = 12, /* Reserved */
+ CMIS_MOD_FLAGS_VENDOR = 13, /* Custom module-level flags */
+
+ /* Table 8-10: Module-Level Monitor Values (bytes 14-25) */
+ CMIS_TEMP = 14, /* S16 Temperature (1/256 deg C) */
+ CMIS_VCC = 16, /* U16 Supply Voltage (100 uV) */
+ CMIS_AUX1 = 18, /* S16 Aux1 Monitor */
+ CMIS_AUX2 = 20, /* S16 Aux2 Monitor */
+ CMIS_AUX3 = 22, /* S16 Aux3 Monitor */
+ CMIS_CUSTOM_MON = 24, /* S16/U16 Custom Monitor */
+
+ /* Table 8-11: Module Global Controls (bytes 26-30) */
+ CMIS_MOD_CTRL = 26, /* Global control bits */
+ CMIS_MOD_CTRL2 = 27, /* Global control bits (cont.) */
+ CMIS_MOD_CTRL3 = 28, /* Global control bits (cont.) */
+ CMIS_MOD_CTRL4 = 29, /* Global control bits (cont.) */
+ CMIS_MOD_CTRL5 = 30, /* Global control bits (cont.) */
+
+ /* Table 8-12: Module Level Masks (bytes 31-36) */
+ CMIS_MOD_MASKS_START = 31, /* Module-level masks start */
+ CMIS_MOD_MASKS_END = 36, /* Module-level masks end */
+
+ /* Table 8-13: CDB Command Status (bytes 37-38) */
+ CMIS_CDB_STATUS1 = 37, /* CDB instance 1 status */
+ CMIS_CDB_STATUS2 = 38, /* CDB instance 2 status */
+
+ /* Table 8-15: Module Active Firmware Version (bytes 39-40) */
+ CMIS_FW_VER_MAJOR = 39, /* Active firmware major version */
+ CMIS_FW_VER_MINOR = 40, /* Active firmware minor version */
+
+ /* Table 8-16: Fault Information (byte 41) */
+ CMIS_FAULT_CAUSE = 41, /* Fault cause for ModuleFault */
+
+ /* Table 8-17: Miscellaneous Status (bytes 42-45) */
+ CMIS_MISC_STATUS_START = 42, /* Password status, etc. */
+ CMIS_MISC_STATUS_END = 45,
+
+ /* Table 8-18: Extended Module Information (bytes 56-63) */
+ CMIS_EXT_MOD_INFO_START = 56,
+ CMIS_EXT_MOD_INFO_END = 63,
+
+ /* Table 8-21: Media Type (byte 85) */
+ CMIS_MEDIA_TYPE = 85, /* MediaType encoding */
+
+ /* Table 8-23: Application Descriptors (bytes 86-117) */
+ CMIS_APP_DESC_START = 86, /* First Application Descriptor */
+ CMIS_APP_DESC1 = 86, /* AppDescriptor 1 (AppSel 1) */
+ CMIS_APP_DESC2 = 90, /* AppDescriptor 2 (AppSel 2) */
+ CMIS_APP_DESC3 = 94, /* AppDescriptor 3 (AppSel 3) */
+ CMIS_APP_DESC4 = 98, /* AppDescriptor 4 (AppSel 4) */
+ CMIS_APP_DESC5 = 102, /* AppDescriptor 5 (AppSel 5) */
+ CMIS_APP_DESC6 = 106, /* AppDescriptor 6 (AppSel 6) */
+ CMIS_APP_DESC7 = 110, /* AppDescriptor 7 (AppSel 7) */
+ CMIS_APP_DESC8 = 114, /* AppDescriptor 8 (AppSel 8) */
+
+ /* Table 8-24: Password (bytes 118-125) */
+ CMIS_PASSWORD_CHANGE = 118, /* Password change entry (4 bytes) */
+ CMIS_PASSWORD_ENTRY = 122, /* Password entry area (4 bytes) */
+
+ /* Table 8-25: Page Mapping (bytes 126-127) */
+ CMIS_BANK_SEL = 126, /* Bank select */
+ CMIS_PAGE_SEL = 127, /* Page select */
+};
+
+/*
+ * Byte 2 (CMIS_MODULE_TYPE) bit definitions (Table 8-5)
+ */
+#define CMIS_MODULE_TYPE_FLAT (1 << 7) /* MemoryModel: 1=flat, 0=paged */
+#define CMIS_MODULE_TYPE_STEPPED (1 << 6) /* SteppedConfigOnly */
+#define CMIS_MODULE_TYPE_MCISPEED_MASK 0x3C /* MciMaxSpeed, bits 5:2 */
+#define CMIS_MODULE_TYPE_MCISPEED_SHIFT 2
+#define CMIS_MODULE_TYPE_AUTOCOM_MASK 0x03 /* AutoCommissioning, bits 1:0 */
+
+/* MciMaxSpeed values (I2CMCI) */
+#define CMIS_MCISPEED_400KHZ 0 /* Up to 400 kHz */
+#define CMIS_MCISPEED_1MHZ 1 /* Up to 1 MHz */
+#define CMIS_MCISPEED_3_4MHZ 2 /* Up to 3.4 MHz */
+
+/* AutoCommissioning values (when SteppedConfigOnly=1) */
+#define CMIS_AUTOCOM_NONE 0x00 /* Neither regular nor hot */
+#define CMIS_AUTOCOM_REGULAR 0x01 /* Only regular (ApplyDPInit) */
+#define CMIS_AUTOCOM_HOT 0x02 /* Only hot (ApplyImmediate) */
+
+/*
+ * Byte 3 (CMIS_MODULE_STATE) bit definitions (Table 8-6)
+ */
+#define CMIS_MODULE_STATE_MASK 0x0E /* ModuleState, bits 3:1 */
+#define CMIS_MODULE_STATE_SHIFT 1
+#define CMIS_MODULE_STATE_INTL 0x01 /* InterruptDeasserted (bit 0) */
+
+/* Table 8-7: Module State Encodings (bits 3:1 of byte 3) */
+#define CMIS_STATE_LOWPWR 1 /* ModuleLowPwr */
+#define CMIS_STATE_PWRUP 2 /* ModulePwrUp */
+#define CMIS_STATE_READY 3 /* ModuleReady */
+#define CMIS_STATE_PWRDN 4 /* ModulePwrDn */
+#define CMIS_STATE_FAULT 5 /* ModuleFault */
+
+/*
+ * Bytes 4-7 (CMIS_FLAGS_BANKn) bit definitions (Table 8-8)
+ * Same layout for all 4 bank bytes.
+ */
+#define CMIS_FLAGS_PAGE2CH (1 << 3) /* Flags on Page 2Ch */
+#define CMIS_FLAGS_PAGE14H (1 << 2) /* Flags on Page 14h */
+#define CMIS_FLAGS_PAGE12H (1 << 1) /* Flags on Page 12h */
+#define CMIS_FLAGS_PAGE11H (1 << 0) /* Flags on Page 11h */
+
+/*
+ * Byte 8 (CMIS_MOD_FLAGS_START) bit definitions (Table 8-9)
+ */
+#define CMIS_FLAG_CDB_COMPLETE2 (1 << 7) /* CdbCmdCompleteFlag2 */
+#define CMIS_FLAG_CDB_COMPLETE1 (1 << 6) /* CdbCmdCompleteFlag1 */
+#define CMIS_FLAG_DP_FW_ERROR (1 << 2) /* DataPathFirmwareErrorFlag */
+#define CMIS_FLAG_MOD_FW_ERROR (1 << 1) /* ModuleFirmwareErrorFlag */
+#define CMIS_FLAG_STATE_CHANGED (1 << 0) /* ModuleStateChangedFlag */
+
+/*
+ * Byte 9 (CMIS_MOD_FLAGS_TEMP_VCC) bit definitions (Table 8-9)
+ */
+#define CMIS_FLAG_VCC_LOW_WARN (1 << 7) /* VccMonLowWarningFlag */
+#define CMIS_FLAG_VCC_HIGH_WARN (1 << 6) /* VccMonHighWarningFlag */
+#define CMIS_FLAG_VCC_LOW_ALM (1 << 5) /* VccMonLowAlarmFlag */
+#define CMIS_FLAG_VCC_HIGH_ALM (1 << 4) /* VccMonHighAlarmFlag */
+#define CMIS_FLAG_TEMP_LOW_WARN (1 << 3) /* TempMonLowWarningFlag */
+#define CMIS_FLAG_TEMP_HIGH_WARN (1 << 2) /* TempMonHighWarningFlag */
+#define CMIS_FLAG_TEMP_LOW_ALM (1 << 1) /* TempMonLowAlarmFlag */
+#define CMIS_FLAG_TEMP_HIGH_ALM (1 << 0) /* TempMonHighAlarmFlag */
+
+/*
+ * Byte 10 (CMIS_MOD_FLAGS_AUX) bit definitions (Table 8-9)
+ */
+#define CMIS_FLAG_AUX2_LOW_WARN (1 << 7)
+#define CMIS_FLAG_AUX2_HIGH_WARN (1 << 6)
+#define CMIS_FLAG_AUX2_LOW_ALM (1 << 5)
+#define CMIS_FLAG_AUX2_HIGH_ALM (1 << 4)
+#define CMIS_FLAG_AUX1_LOW_WARN (1 << 3)
+#define CMIS_FLAG_AUX1_HIGH_WARN (1 << 2)
+#define CMIS_FLAG_AUX1_LOW_ALM (1 << 1)
+#define CMIS_FLAG_AUX1_HIGH_ALM (1 << 0)
+
+/*
+ * Byte 11 (CMIS_MOD_FLAGS_CUSTOM) bit definitions (Table 8-9)
+ */
+#define CMIS_FLAG_CUST_LOW_WARN (1 << 7)
+#define CMIS_FLAG_CUST_HIGH_WARN (1 << 6)
+#define CMIS_FLAG_CUST_LOW_ALM (1 << 5)
+#define CMIS_FLAG_CUST_HIGH_ALM (1 << 4)
+#define CMIS_FLAG_AUX3_LOW_WARN (1 << 3)
+#define CMIS_FLAG_AUX3_HIGH_WARN (1 << 2)
+#define CMIS_FLAG_AUX3_LOW_ALM (1 << 1)
+#define CMIS_FLAG_AUX3_HIGH_ALM (1 << 0)
+
+/*
+ * Byte 26 (CMIS_MOD_CTRL) bit definitions (Table 8-11)
+ */
+#define CMIS_CTRL_BANK_BCAST (1 << 7) /* BankBroadcastEnable */
+#define CMIS_CTRL_LOWPWR_HW (1 << 6) /* LowPwrAllowRequestHW */
+#define CMIS_CTRL_SQUELCH_METHOD (1 << 5) /* SquelchMethodSelect */
+#define CMIS_CTRL_LOWPWR_SW (1 << 4) /* LowPwrRequestSW */
+#define CMIS_CTRL_SW_RESET (1 << 3) /* SoftwareReset */
+
+/*
+ * Byte 27 (CMIS_MOD_CTRL2) bit definitions (Table 8-11)
+ */
+#define CMIS_CTRL2_MCISPEED_MASK 0x0F /* MciSpeedConfiguration, bits 3:0 */
+
+/*
+ * Bytes 31-36 mask bits mirror bytes 8-13 flag bits (Table 8-12)
+ * Use the same bit positions as CMIS_FLAG_* above.
+ */
+
+/*
+ * Bytes 37-38 (CDB Status) bit definitions (Table 8-14)
+ */
+#define CMIS_CDB_BUSY (1 << 7) /* CdbIsBusy */
+#define CMIS_CDB_FAILED (1 << 6) /* CdbHasFailed */
+#define CMIS_CDB_RESULT_MASK 0x3F /* CdbCommandResult, bits 5:0 */
+
+/* Table 8-20: Media Type Encodings */
+#define CMIS_MEDIA_TYPE_UNDEF 0x00 /* Undefined */
+#define CMIS_MEDIA_TYPE_MMF 0x01 /* Optical: MMF */
+#define CMIS_MEDIA_TYPE_SMF 0x02 /* Optical: SMF */
+#define CMIS_MEDIA_TYPE_COPPER 0x03 /* Passive/Active Copper */
+#define CMIS_MEDIA_TYPE_ACTIVE 0x04 /* Active Cable */
+#define CMIS_MEDIA_TYPE_BASET 0x05 /* BASE-T */
+
+/* Application Descriptor constants */
+#define CMIS_APP_DESC_SIZE 4 /* Bytes per descriptor */
+#define CMIS_MAX_APP_DESC 8 /* Max descriptors in lower memory */
+
+/* Table 8-22: Offsets within an Application Descriptor */
+#define CMIS_APP_HOST_IF_ID 0 /* HostInterfaceID */
+#define CMIS_APP_MEDIA_IF_ID 1 /* MediaInterfaceID */
+#define CMIS_APP_LANE_COUNT 2 /* Host[7:4], Media[3:0] */
+#define CMIS_APP_HOST_ASSIGN 3 /* HostLaneAssignment */
+#define CMIS_APP_HOST_LANES_MASK 0xF0 /* HostLaneCount, bits 7:4 */
+#define CMIS_APP_HOST_LANES_SHIFT 4
+#define CMIS_APP_MEDIA_LANES_MASK 0x0F /* MediaLaneCount, bits 3:0 */
+
+/*
+ * Table 8-26: Page 00h - Administrative Information
+ * Accessed with page=0x00, bank=0.
+ */
+enum {
+ CMIS_P0_ID = 128, /* SFF-8024 Identifier copy */
+ CMIS_P0_VENDOR_NAME = 129, /* Vendor name (16 bytes, ASCII) */
+ CMIS_P0_VENDOR_OUI = 145, /* Vendor IEEE OUI (3 bytes) */
+ CMIS_P0_VENDOR_PN = 148, /* Part number (16 bytes, ASCII) */
+ CMIS_P0_VENDOR_REV = 164, /* Vendor revision (2 bytes) */
+ CMIS_P0_VENDOR_SN = 166, /* Serial number (16 bytes, ASCII) */
+ CMIS_P0_DATE_CODE = 182, /* Date code (8 bytes: YYMMDDLL) */
+ CMIS_P0_CLEI = 190, /* CLEI code (10 bytes, ASCII) */
+ CMIS_P0_MOD_POWER = 200, /* Module power class */
+ CMIS_P0_MAX_POWER = 201, /* Max power (multiples of 0.25W) */
+ CMIS_P0_CABLE_LEN = 202, /* Cable assembly link length */
+ CMIS_P0_CONNECTOR = 203, /* Connector type (SFF-8024) */
+ CMIS_P0_COPPER_ATTEN = 204, /* Copper cable attenuation (6 bytes) */
+ CMIS_P0_MEDIA_LANE_INFO = 210, /* Supported near end media lanes */
+ CMIS_P0_CABLE_ASM_INFO = 211, /* Far end breakout info */
+ CMIS_P0_MEDIA_TECH = 212, /* Media interface technology */
+ CMIS_P0_MCI_ADVERT = 213, /* MCI advertisement (2 bytes) */
+ CMIS_P0_PAGE_CKSUM = 222, /* Page checksum (bytes 128-221) */
+ CMIS_P0_CUSTOM = 223, /* Custom (33 bytes) */
+};
+
+/*
+ * Table 8-82: Page 11h - Lane Status and Data Path Status
+ * Accessed with page=0x11, bank=0 (lanes 1-8).
+ */
+enum {
+ /* Table 8-83: Data Path States (bytes 128-131) */
+ CMIS_P11_DPSTATE_12 = 128, /* DPState for host lanes 1-2 */
+ CMIS_P11_DPSTATE_34 = 129, /* DPState for host lanes 3-4 */
+ CMIS_P11_DPSTATE_56 = 130, /* DPState for host lanes 5-6 */
+ CMIS_P11_DPSTATE_78 = 131, /* DPState for host lanes 7-8 */
+
+ /* Table 8-85: Lane Output Status (bytes 132-133) */
+ CMIS_P11_OUTPUT_RX = 132, /* OutputStatusRx per lane */
+ CMIS_P11_OUTPUT_TX = 133, /* OutputStatusTx per lane */
+
+ /* Table 8-86: State Changed Flags (bytes 134-135) */
+ CMIS_P11_DPSTATE_CHGD = 134, /* DPStateChanged flags */
+ CMIS_P11_OUTPUT_CHGD_TX = 135, /* OutputStatusChangedTx flags*/
+
+ /* Table 8-87: Lane-Specific Tx Flags (bytes 136-141) */
+ CMIS_P11_TX_FAULT = 136, /* TxFault per lane */
+ CMIS_P11_TX_LOS = 137, /* TxLOS per lane */
+ CMIS_P11_TX_CDR_LOL = 138, /* TxCDRLOL per lane */
+ CMIS_P11_TX_ADPT_EQ_FAIL = 139, /* TxAdaptEqFail per lane */
+ CMIS_P11_TX_PWR_HIGH_ALM = 140, /* TxPowerHighAlarm per lane */
+ CMIS_P11_TX_PWR_LOW_ALM = 141, /* TxPowerLowAlarm per lane */
+ CMIS_P11_TX_BIAS_HIGH_ALM = 142, /* TxBiasHighAlarm per lane */
+ CMIS_P11_TX_BIAS_LOW_ALM = 143, /* TxBiasLowAlarm per lane */
+ CMIS_P11_TX_PWR_HIGH_WARN = 144, /* TxPowerHighWarning per lane*/
+ CMIS_P11_TX_PWR_LOW_WARN = 145, /* TxPowerLowWarning per lane */
+ CMIS_P11_TX_BIAS_HIGH_WARN = 146, /* TxBiasHighWarning per lane */
+ CMIS_P11_TX_BIAS_LOW_WARN = 147, /* TxBiasLowWarning per lane */
+
+ /* Table 8-88: Rx Flags (bytes 148-153) */
+ CMIS_P11_RX_LOS = 148, /* RxLOS per lane */
+ CMIS_P11_RX_CDR_LOL = 149, /* RxCDRLOL per lane */
+ CMIS_P11_RX_PWR_HIGH_ALM = 150, /* RxPowerHighAlarm per lane */
+ CMIS_P11_RX_PWR_LOW_ALM = 151, /* RxPowerLowAlarm per lane */
+ CMIS_P11_RX_PWR_HIGH_WARN = 152, /* RxPowerHighWarning per lane*/
+ CMIS_P11_RX_PWR_LOW_WARN = 153, /* RxPowerLowWarning per lane */
+
+ /* Table 8-89: Lane-Specific Monitors (bytes 154-201) */
+ CMIS_P11_TX_PWR_1 = 154, /* U16 Tx optical pwr, lane 1 */
+ CMIS_P11_TX_PWR_2 = 156, /* (0.1 uW increments) */
+ CMIS_P11_TX_PWR_3 = 158,
+ CMIS_P11_TX_PWR_4 = 160,
+ CMIS_P11_TX_PWR_5 = 162,
+ CMIS_P11_TX_PWR_6 = 164,
+ CMIS_P11_TX_PWR_7 = 166,
+ CMIS_P11_TX_PWR_8 = 168,
+ CMIS_P11_TX_BIAS_1 = 170, /* U16 Tx bias current, lane 1*/
+ CMIS_P11_TX_BIAS_2 = 172, /* (2 uA increments) */
+ CMIS_P11_TX_BIAS_3 = 174,
+ CMIS_P11_TX_BIAS_4 = 176,
+ CMIS_P11_TX_BIAS_5 = 178,
+ CMIS_P11_TX_BIAS_6 = 180,
+ CMIS_P11_TX_BIAS_7 = 182,
+ CMIS_P11_TX_BIAS_8 = 184,
+ CMIS_P11_RX_PWR_1 = 186, /* U16 Rx input power, lane 1 */
+ CMIS_P11_RX_PWR_2 = 188, /* (0.1 uW increments) */
+ CMIS_P11_RX_PWR_3 = 190,
+ CMIS_P11_RX_PWR_4 = 192,
+ CMIS_P11_RX_PWR_5 = 194,
+ CMIS_P11_RX_PWR_6 = 196,
+ CMIS_P11_RX_PWR_7 = 198,
+ CMIS_P11_RX_PWR_8 = 200,
+
+ /* Table 8-90: Config Command Status (bytes 202-205) */
+ CMIS_P11_CONFIG_STAT_12 = 202, /* ConfigStatus lanes 1-2 */
+ CMIS_P11_CONFIG_STAT_34 = 203, /* ConfigStatus lanes 3-4 */
+ CMIS_P11_CONFIG_STAT_56 = 204, /* ConfigStatus lanes 5-6 */
+ CMIS_P11_CONFIG_STAT_78 = 205, /* ConfigStatus lanes 7-8 */
+
+ /* Table 8-93: Active Control Set (bytes 206-234) */
+ CMIS_P11_ACS_DPCONFIG1 = 206, /* DPConfigLane1 (AppSel[7:4])*/
+ CMIS_P11_ACS_DPCONFIG2 = 207, /* DPConfigLane2 */
+ CMIS_P11_ACS_DPCONFIG3 = 208, /* DPConfigLane3 */
+ CMIS_P11_ACS_DPCONFIG4 = 209, /* DPConfigLane4 */
+ CMIS_P11_ACS_DPCONFIG5 = 210, /* DPConfigLane5 */
+ CMIS_P11_ACS_DPCONFIG6 = 211, /* DPConfigLane6 */
+ CMIS_P11_ACS_DPCONFIG7 = 212, /* DPConfigLane7 */
+ CMIS_P11_ACS_DPCONFIG8 = 213, /* DPConfigLane8 */
+ CMIS_P11_ACS_TX_START = 214, /* Provisioned Tx Controls */
+ CMIS_P11_ACS_TX_END = 225,
+ CMIS_P11_ACS_RX_START = 226, /* Provisioned Rx Controls */
+ CMIS_P11_ACS_RX_END = 234,
+
+ /* Table 8-96: Data Path Conditions (bytes 235-239) */
+ CMIS_P11_DP_COND_START = 235,
+ CMIS_P11_DP_COND_END = 239,
+
+ /* Table 8-97: Media Lane Mapping (bytes 240-255) */
+ CMIS_P11_MEDIA_MAP_START = 240,
+ CMIS_P11_MEDIA_MAP_END = 255,
+};
+
+/*
+ * Per-lane bit positions for Page 11h flag/status registers.
+ * Bytes 132-153 use bit N for lane N+1 (bit 7 = lane 8, bit 0 = lane 1).
+ */
+#define CMIS_LANE8 (1 << 7)
+#define CMIS_LANE7 (1 << 6)
+#define CMIS_LANE6 (1 << 5)
+#define CMIS_LANE5 (1 << 4)
+#define CMIS_LANE4 (1 << 3)
+#define CMIS_LANE3 (1 << 2)
+#define CMIS_LANE2 (1 << 1)
+#define CMIS_LANE1 (1 << 0)
+
+/*
+ * DPState encoding within bytes 128-131 (Table 8-83).
+ * Each byte holds two 4-bit DPState fields:
+ * bits 7:4 = even lane, bits 3:0 = odd lane.
+ */
+#define CMIS_DPSTATE_HI_MASK 0xF0 /* Upper nibble (even lane) */
+#define CMIS_DPSTATE_HI_SHIFT 4
+#define CMIS_DPSTATE_LO_MASK 0x0F /* Lower nibble (odd lane) */
+
+/* Table 8-84: Data Path State Encoding */
+#define CMIS_DPSTATE_DEACTIVATED 1 /* DPDeactivated (or unused) */
+#define CMIS_DPSTATE_INIT 2 /* DPInit */
+#define CMIS_DPSTATE_DEINIT 3 /* DPDeinit */
+#define CMIS_DPSTATE_ACTIVATED 4 /* DPActivated */
+#define CMIS_DPSTATE_TXTURNON 5 /* DPTxTurnOn */
+#define CMIS_DPSTATE_TXTURNOFF 6 /* DPTxTurnOff */
+#define CMIS_DPSTATE_INITIALIZED 7 /* DPInitialized */
+
+/*
+ * ConfigStatus encoding within bytes 202-205 (Table 8-90/91).
+ * Each byte holds two 4-bit status fields, same nibble layout as DPState.
+ */
+#define CMIS_CFGSTAT_UNDEFINED 0x0 /* Undefined */
+#define CMIS_CFGSTAT_SUCCESS 0x1 /* ConfigSuccess */
+#define CMIS_CFGSTAT_REJECTED 0x2 /* ConfigRejected */
+#define CMIS_CFGSTAT_REJECTEDINV 0x3 /* ConfigRejectedInvalidAppSel*/
+#define CMIS_CFGSTAT_INPROGRESS 0x4 /* ConfigInProgress */
+#define CMIS_CFGSTAT_REJECTEDLANE 0x5 /* ConfigRejectedInvalidLane */
+#define CMIS_CFGSTAT_REJECTEDEQ 0x6 /* ConfigRejectedInvalidEq */
+
+/* DPConfigLane (CMIS_P11_ACS_DPCONFIGn) bit definitions (Table 8-92/93) */
+#define CMIS_ACS_APPSEL_MASK 0xF0 /* AppSel code, bits 7:4 */
+#define CMIS_ACS_APPSEL_SHIFT 4
+#define CMIS_ACS_DATAPATH_MASK 0x0F /* DataPathID, bits 3:0 */
+
+/*
+ * Page 00h bit definitions
+ */
+
+/* Byte 200 (CMIS_P0_MOD_POWER) bit definitions (Table 8-31) */
+#define CMIS_POWER_CLASS_MASK 0xE0 /* ModulePowerClass, bits 7:5 */
+#define CMIS_POWER_CLASS_SHIFT 5
+#define CMIS_POWER_CLASS_1 0 /* <=1.5W */
+#define CMIS_POWER_CLASS_2 1 /* <=3.5W */
+#define CMIS_POWER_CLASS_3 2 /* <=7.0W */
+#define CMIS_POWER_CLASS_4 3 /* <=8.0W */
+#define CMIS_POWER_CLASS_5 4 /* <=10.0W */
+#define CMIS_POWER_CLASS_6 5 /* <=12.0W */
+#define CMIS_POWER_CLASS_7 6 /* <=14.0W */
+#define CMIS_POWER_CLASS_8 7 /* >14.0W, see MaxPower byte */
+#define CMIS_POWER_MAX_IN_BYTE (1 << 4) /* MaxPowerOverride: byte 201*/
+
+/* Byte 202 (CMIS_P0_CABLE_LEN) bit definitions (Table 8-32) */
+#define CMIS_CABLE_LEN_MULT_MASK 0xC0 /* LengthMultiplier, bits 7:6 */
+#define CMIS_CABLE_LEN_MULT_SHIFT 6
+#define CMIS_CABLE_LEN_VAL_MASK 0x3F /* Length value, bits 5:0 */
+#define CMIS_CABLE_LEN_MULT_01 0 /* x 0.1m */
+#define CMIS_CABLE_LEN_MULT_1 1 /* x 1m */
+#define CMIS_CABLE_LEN_MULT_10 2 /* x 10m */
+#define CMIS_CABLE_LEN_MULT_100 3 /* x 100m */
+
+/* Lane monitor stride (each monitor is U16 = 2 bytes per lane) */
+#define CMIS_LANE_MON_SIZE 2
+#define CMIS_MAX_LANES 8
+
+#endif /* !_NET_CMIS_H_ */
diff --git a/sys/net/if.c b/sys/net/if.c
index 519b23750b52..8a148ba0fd06 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -479,6 +479,8 @@ vnet_if_return(const void *unused __unused)
i = 0;
+ /* The lock has already been aquired in vnet_destroy() */
+ sx_assert(&ifnet_detach_sxlock, SX_XLOCKED);
/*
* We need to protect our access to the V_ifnet tailq. Ordinarily we'd
* enter NET_EPOCH, but that's not possible, because if_vmove() calls
@@ -507,9 +509,7 @@ vnet_if_return(const void *unused __unused)
IFNET_WUNLOCK();
for (int j = 0; j < i; j++) {
- sx_xlock(&ifnet_detach_sxlock);
if_vmove(pending[j], pending[j]->if_home_vnet);
- sx_xunlock(&ifnet_detach_sxlock);
}
free(pending, M_IFNET);
@@ -1191,26 +1191,39 @@ if_vmove(struct ifnet *ifp, struct vnet *new_vnet)
* Move an ifnet to or from another child prison/vnet, specified by the jail id.
*/
static int
-if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid)
+if_vmove_loan(struct thread *td, char *ifname, int jid)
{
struct prison *pr;
- struct ifnet *difp;
+ struct ifnet *ifp, *difp;
bool found;
+ MPASS(curthread == td);
+ MPASS(curvnet == TD_TO_VNET(td));
+
+ /*
+ * We check the existence of the interface, and will later try to
+ * unlink it from the "active" list, so it is sufficient to only
+ * hold a weak reference to it.
+ * Be aware that it is unsafe to access any member of it, until it
+ * is proven to be safe to ( say it was on the "active" list ).
+ */
+ ifp = ifunit(ifname);
+ if (ifp == NULL)
+ return (ENXIO);
+
/* Try to find the prison within our visibility. */
sx_slock(&allprison_lock);
pr = prison_find_child(td->td_ucred->cr_prison, jid);
sx_sunlock(&allprison_lock);
if (pr == NULL)
return (ENXIO);
- prison_hold_locked(pr);
- mtx_unlock(&pr->pr_mtx);
-
- /* Do not try to move the iface from and to the same prison. */
- if (pr->pr_vnet == ifp->if_vnet) {
- prison_free(pr);
+ /* Do not try to move the iface from and to the same vnet. */
+ if (pr->pr_vnet == TD_TO_VNET(td)) {
+ mtx_unlock(&pr->pr_mtx);
return (EEXIST);
}
+ prison_hold_locked(pr);
+ mtx_unlock(&pr->pr_mtx);
/* Make sure the named iface does not exists in the dst. prison/vnet. */
/* XXX Lock interfaces to avoid races. */
@@ -2260,6 +2273,8 @@ const struct ifcap_nv_bit_name ifcap2_nv_bit_names[] = {
CAP2NV(RXTLS4),
CAP2NV(RXTLS6),
CAP2NV(IPSEC_OFFLOAD),
+ CAP2NV(GENEVE_HWCSUM),
+ CAP2NV(GENEVE_HWTSO),
{0, NULL}
};
#undef CAPNV
@@ -2580,15 +2595,6 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td)
error = if_rename(ifp, new_name);
break;
-#ifdef VIMAGE
- case SIOCSIFVNET:
- error = priv_check(td, PRIV_NET_SETIFVNET);
- if (error)
- return (error);
- error = if_vmove_loan(td, ifp, ifr->ifr_name, ifr->ifr_jid);
- break;
-#endif
-
case SIOCSIFMETRIC:
error = priv_check(td, PRIV_NET_SETIFMETRIC);
if (error)
@@ -2876,6 +2882,12 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td)
ifr = (struct ifreq *)data;
switch (cmd) {
#ifdef VIMAGE
+ case SIOCSIFVNET:
+ error = priv_check(td, PRIV_NET_SETIFVNET);
+ if (error == 0)
+ error = if_vmove_loan(td, ifr->ifr_name, ifr->ifr_jid);
+ goto out_noref;
+
case SIOCSIFRVNET:
error = priv_check(td, PRIV_NET_SETIFVNET);
if (error == 0)
@@ -2894,11 +2906,8 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td)
case SIOCIFDESTROY:
error = priv_check(td, PRIV_NET_IFDESTROY);
- if (error == 0) {
- sx_xlock(&ifnet_detach_sxlock);
+ if (error == 0)
error = if_clone_destroy(ifr->ifr_name);
- sx_xunlock(&ifnet_detach_sxlock);
- }
goto out_noref;
case SIOCIFGCLONERS:
diff --git a/sys/net/if.h b/sys/net/if.h
index 1b47237e46bb..4bb6a2659ce7 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -255,7 +255,9 @@ struct if_data {
#define IFCAP_B_RXTLS4 32 /* can do TLS receive for TCP */
#define IFCAP_B_RXTLS6 33 /* can do TLS receive for TCP6 */
#define IFCAP_B_IPSEC_OFFLOAD 34 /* inline IPSEC offload */
-#define __IFCAP_B_SIZE 35
+#define IFCAP_B_GENEVE_HWCSUM 35 /* can do IFCAN_HWCSUM on GENEVE */
+#define IFCAP_B_GENEVE_HWTSO 36 /* can do IFCAP_TSO on GENEVE */
+#define __IFCAP_B_SIZE 37
#define IFCAP_B_MAX (__IFCAP_B_MAX - 1)
#define IFCAP_B_SIZE (__IFCAP_B_SIZE)
@@ -299,6 +301,8 @@ struct if_data {
#define IFCAP2_RXTLS4 (IFCAP_B_RXTLS4 - 32)
#define IFCAP2_RXTLS6 (IFCAP_B_RXTLS6 - 32)
#define IFCAP2_IPSEC_OFFLOAD (IFCAP_B_IPSEC_OFFLOAD - 32)
+#define IFCAP2_GENEVE_HWCSUM (IFCAP_B_GENEVE_HWCSUM - 32)
+#define IFCAP2_GENEVE_HWTSO (IFCAP_B_GENEVE_HWTSO - 32)
#define IFCAP2_BIT(x) (1UL << (x))
diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c
index 4bc04130da1e..db3db78c1b76 100644
--- a/sys/net/if_clone.c
+++ b/sys/net/if_clone.c
@@ -442,6 +442,14 @@ if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags
int err;
/*
+ * XXXZL: To avoid racing with if_vmove() so that we will have
+ * stable if_vnet.
+ * This have a good effect, that is, the destroying of tightly
+ * coupled cloned interfaces such as epair(4) is serialized,
+ * although the driver is responsible to take care of that.
+ */
+ sx_assert(&ifnet_detach_sxlock, SA_XLOCKED);
+ /*
* Given that the cloned ifnet might be attached to a different
* vnet from where its cloner was registered, we have to
* switch to the vnet context of the target vnet.
@@ -467,7 +475,12 @@ if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags
int
if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp)
{
- return (if_clone_destroyif_flags(ifc, ifp, 0));
+ int err;
+
+ sx_xlock(&ifnet_detach_sxlock);
+ err = if_clone_destroyif_flags(ifc, ifp, 0);
+ sx_xunlock(&ifnet_detach_sxlock);
+ return (err);
}
static struct if_clone *
@@ -670,9 +683,11 @@ if_clone_detach(struct if_clone *ifc)
V_if_cloners_count--;
IF_CLONERS_UNLOCK();
+ sx_xlock(&ifnet_detach_sxlock);
/* destroy all interfaces for this cloner */
while (!LIST_EMPTY(&ifc->ifc_iflist))
if_clone_destroyif_flags(ifc, LIST_FIRST(&ifc->ifc_iflist), IFC_F_FORCE);
+ sx_xunlock(&ifnet_detach_sxlock);
IF_CLONE_REMREF(ifc);
}
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 812a31595df9..bbaf798b49f2 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -475,27 +475,6 @@ ether_output_frame(struct ifnet *ifp, struct mbuf *m)
return (0);
}
-#ifdef EXPERIMENTAL
-#if defined(INET6) && defined(INET)
- /* draft-ietf-6man-ipv6only-flag */
- /* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */
- if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
- struct ether_header *eh;
-
- eh = mtod(m, struct ether_header *);
- switch (ntohs(eh->ether_type)) {
- case ETHERTYPE_IP:
- case ETHERTYPE_ARP:
- case ETHERTYPE_REVARP:
- m_freem(m);
- return (EAFNOSUPPORT);
- /* NOTREACHED */
- break;
- };
- }
-#endif
-#endif
-
/*
* Queue message on interface, update output statistics if successful,
* and start output if interface not yet active.
@@ -541,24 +520,6 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
etype = ntohs(eh->ether_type);
random_harvest_queue_ether(m, sizeof(*m));
-#ifdef EXPERIMENTAL
-#if defined(INET6) && defined(INET)
- /* draft-ietf-6man-ipv6only-flag */
- /* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */
- if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
- switch (etype) {
- case ETHERTYPE_IP:
- case ETHERTYPE_ARP:
- case ETHERTYPE_REVARP:
- m_freem(m);
- return;
- /* NOTREACHED */
- break;
- };
- }
-#endif
-#endif
-
CURVNET_SET_QUIET(ifp->if_vnet);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
diff --git a/sys/net/if_geneve.c b/sys/net/if_geneve.c
new file mode 100644
index 000000000000..ab8b313e860a
--- /dev/null
+++ b/sys/net/if_geneve.c
@@ -0,0 +1,4009 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/hash.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/refcount.h>
+#include <sys/rmlock.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sdt.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/counter.h>
+#include <sys/jail.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_private.h>
+#include <net/if_arp.h>
+#include <net/if_clone.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <net/route/nhop.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/scope6_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/in_fib.h>
+#include <netinet6/in6_fib.h>
+#include <netinet/ip_ecn.h>
+#include <net/if_geneve.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_ctl.h>
+#include <netlink/netlink_var.h>
+#include <netlink/netlink_route.h>
+#include <netlink/route/route_var.h>
+
+#include <security/mac/mac_framework.h>
+
+SDT_PROVIDER_DEFINE(if_geneve);
+
+struct geneve_softc;
+LIST_HEAD(geneve_softc_head, geneve_softc);
+
+static struct sx geneve_sx;
+SX_SYSINIT(geneve, &geneve_sx, "GENEVE global start/stop lock");
+
+static unsigned geneve_osd_jail_slot;
+
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+};
+
+struct geneve_socket_mc_info {
+ union sockaddr_union gnvsomc_saddr;
+ union sockaddr_union gnvsomc_gaddr;
+ int gnvsomc_ifidx;
+ int gnvsomc_users;
+};
+
+/* The maximum MTU of encapsulated geneve packet. */
+#define GENEVE_MAX_L3MTU (IP_MAXPACKET - \
+ 60 /* Maximum IPv4 header len */ - \
+ sizeof(struct udphdr) - \
+ sizeof(struct genevehdr))
+#define GENEVE_MAX_MTU (GENEVE_MAX_L3MTU - \
+ ETHER_HDR_LEN - ETHER_VLAN_ENCAP_LEN)
+
+#define GENEVE_BASIC_IFCAPS (IFCAP_LINKSTATE | IFCAP_JUMBO_MTU | IFCAP_NV)
+
+#define GENEVE_VERSION 0
+#define GENEVE_VNI_MASK (GENEVE_VNI_MAX - 1)
+
+#define GENEVE_HDR_VNI_SHIFT 8
+
+#define GENEVE_SO_MC_MAX_GROUPS 32
+
+#define GENEVE_SO_VNI_HASH_SHIFT 6
+#define GENEVE_SO_VNI_HASH_SIZE (1 << GENEVE_SO_VNI_HASH_SHIFT)
+#define GENEVE_SO_VNI_HASH(_vni) ((_vni) % GENEVE_SO_VNI_HASH_SIZE)
+
+struct geneve_socket {
+ struct socket *gnvso_sock;
+ struct rmlock gnvso_lock;
+ u_int gnvso_refcnt;
+ union sockaddr_union gnvso_laddr;
+ LIST_ENTRY(geneve_socket) gnvso_entry;
+ struct geneve_softc_head gnvso_vni_hash[GENEVE_SO_VNI_HASH_SIZE];
+ struct geneve_socket_mc_info gnvso_mc[GENEVE_SO_MC_MAX_GROUPS];
+};
+
+#define GENEVE_SO_RLOCK(_gnvso, _p) rm_rlock(&(_gnvso)->gnvso_lock, (_p))
+#define GENEVE_SO_RUNLOCK(_gnvso, _p) rm_runlock(&(_gnvso)->gnvso_lock, (_p))
+#define GENEVE_SO_WLOCK(_gnvso) rm_wlock(&(_gnvso)->gnvso_lock)
+#define GENEVE_SO_WUNLOCK(_gnvso) rm_wunlock(&(_gnvso)->gnvso_lock)
+#define GENEVE_SO_LOCK_ASSERT(_gnvso) \
+ rm_assert(&(_gnvso)->gnvso_lock, RA_LOCKED)
+#define GENEVE_SO_LOCK_WASSERT(_gnvso) \
+ rm_assert(&(_gnvso)->gnvso_lock, RA_WLOCKED)
+
+#define GENEVE_SO_ACQUIRE(_gnvso) refcount_acquire(&(_gnvso)->gnvso_refcnt)
+#define GENEVE_SO_RELEASE(_gnvso) refcount_release(&(_gnvso)->gnvso_refcnt)
+
+struct gnv_ftable_entry {
+ LIST_ENTRY(gnv_ftable_entry) gnvfe_hash;
+ uint16_t gnvfe_flags;
+ uint8_t gnvfe_mac[ETHER_ADDR_LEN];
+ union sockaddr_union gnvfe_raddr;
+ time_t gnvfe_expire;
+};
+
+#define GENEVE_FE_FLAG_DYNAMIC 0x01
+#define GENEVE_FE_FLAG_STATIC 0x02
+
+#define GENEVE_FE_IS_DYNAMIC(_fe) \
+ ((_fe)->gnvfe_flags & GENEVE_FE_FLAG_DYNAMIC)
+
+#define GENEVE_SC_FTABLE_SHIFT 9
+#define GENEVE_SC_FTABLE_SIZE (1 << GENEVE_SC_FTABLE_SHIFT)
+#define GENEVE_SC_FTABLE_MASK (GENEVE_SC_FTABLE_SIZE - 1)
+#define GENEVE_SC_FTABLE_HASH(_sc, _mac) \
+ (geneve_mac_hash(_sc, _mac) % GENEVE_SC_FTABLE_SIZE)
+
+LIST_HEAD(geneve_ftable_head, gnv_ftable_entry);
+
+struct geneve_statistics {
+ uint32_t ftable_nospace;
+ uint32_t ftable_lock_upgrade_failed;
+ counter_u64_t txcsum;
+ counter_u64_t tso;
+ counter_u64_t rxcsum;
+};
+
+struct geneve_softc {
+ LIST_ENTRY(geneve_softc) gnv_entry;
+
+ struct ifnet *gnv_ifp;
+ uint32_t gnv_flags;
+#define GENEVE_FLAG_INIT 0x0001
+#define GENEVE_FLAG_RUNNING 0x0002
+#define GENEVE_FLAG_TEARDOWN 0x0004
+#define GENEVE_FLAG_LEARN 0x0008
+#define GENEVE_FLAG_USER_MTU 0x0010
+#define GENEVE_FLAG_TTL_INHERIT 0x0020
+#define GENEVE_FLAG_DSCP_INHERIT 0x0040
+#define GENEVE_FLAG_COLLECT_METADATA 0x0080
+
+ int gnv_reqcap;
+ int gnv_reqcap2;
+ struct geneve_socket *gnv_sock;
+ union sockaddr_union gnv_src_addr;
+ union sockaddr_union gnv_dst_addr;
+ uint32_t gnv_fibnum;
+ uint32_t gnv_vni;
+ uint32_t gnv_port_hash_key;
+ uint16_t gnv_proto;
+ uint16_t gnv_min_port;
+ uint16_t gnv_max_port;
+ uint8_t gnv_ttl;
+ enum ifla_geneve_df gnv_df;
+
+ /* Lookup table from MAC address to forwarding entry. */
+ uint32_t gnv_ftable_cnt;
+ uint32_t gnv_ftable_max;
+ uint32_t gnv_ftable_timeout;
+ uint32_t gnv_ftable_hash_key;
+ struct geneve_ftable_head *gnv_ftable;
+
+ /* Derived from gnv_dst_addr. */
+ struct gnv_ftable_entry gnv_default_fe;
+
+ struct ip_moptions *gnv_im4o;
+ struct ip6_moptions *gnv_im6o;
+
+ struct rmlock gnv_lock;
+ volatile u_int gnv_refcnt;
+
+ int gnv_so_mc_index;
+ struct geneve_statistics gnv_stats;
+ struct callout gnv_callout;
+ struct ether_addr gnv_hwaddr;
+ int gnv_mc_ifindex;
+ struct ifnet *gnv_mc_ifp;
+ struct ifmedia gnv_media;
+ char gnv_mc_ifname[IFNAMSIZ];
+
+ /* For rate limiting errors on the tx fast path. */
+ struct timeval err_time;
+ int err_pps;
+};
+
+#define GENEVE_RLOCK(_sc, _p) rm_rlock(&(_sc)->gnv_lock, (_p))
+#define GENEVE_RUNLOCK(_sc, _p) rm_runlock(&(_sc)->gnv_lock, (_p))
+#define GENEVE_WLOCK(_sc) rm_wlock(&(_sc)->gnv_lock)
+#define GENEVE_WUNLOCK(_sc) rm_wunlock(&(_sc)->gnv_lock)
+#define GENEVE_LOCK_WOWNED(_sc) rm_wowned(&(_sc)->gnv_lock)
+#define GENEVE_LOCK_ASSERT(_sc) rm_assert(&(_sc)->gnv_lock, RA_LOCKED)
+#define GENEVE_LOCK_WASSERT(_sc) rm_assert(&(_sc)->gnv_lock, RA_WLOCKED)
+#define GENEVE_UNLOCK(_sc, _p) do { \
+ if (GENEVE_LOCK_WOWNED(_sc)) \
+ GENEVE_WUNLOCK(_sc); \
+ else \
+ GENEVE_RUNLOCK(_sc, _p); \
+} while (0)
+
+#define GENEVE_ACQUIRE(_sc) refcount_acquire(&(_sc)->gnv_refcnt)
+#define GENEVE_RELEASE(_sc) refcount_release(&(_sc)->gnv_refcnt)
+
+#define SATOCONSTSIN(sa) ((const struct sockaddr_in *)(sa))
+#define SATOCONSTSIN6(sa) ((const struct sockaddr_in6 *)(sa))
+
+struct geneve_pkt_info {
+ u_int isr;
+ uint16_t ethertype;
+ uint8_t ecn;
+ uint8_t ttl;
+};
+
+struct nl_parsed_geneve {
+ /* essential */
+ uint32_t ifla_vni;
+ uint16_t ifla_proto;
+ struct sockaddr *ifla_local;
+ struct sockaddr *ifla_remote;
+ uint16_t ifla_local_port;
+ uint16_t ifla_remote_port;
+
+ /* optional */
+ struct ifla_geneve_port_range ifla_port_range;
+ enum ifla_geneve_df ifla_df;
+ uint8_t ifla_ttl;
+ bool ifla_ttl_inherit;
+ bool ifla_dscp_inherit;
+ bool ifla_external;
+
+ /* l2 specific */
+ bool ifla_ftable_learn;
+ bool ifla_ftable_flush;
+ uint32_t ifla_ftable_max;
+ uint32_t ifla_ftable_timeout;
+ uint32_t ifla_ftable_count; /* read-only */
+
+ /* multicast specific */
+ char *ifla_mc_ifname;
+ uint32_t ifla_mc_ifindex; /* read-only */
+};
+
+/* The multicast-based learning parts of the code are taken from if_vxlan */
+static int geneve_ftable_addr_cmp(const uint8_t *, const uint8_t *);
+static void geneve_ftable_init(struct geneve_softc *);
+static void geneve_ftable_fini(struct geneve_softc *);
+static void geneve_ftable_flush(struct geneve_softc *, int);
+static void geneve_ftable_expire(struct geneve_softc *);
+static int geneve_ftable_update_locked(struct geneve_softc *,
+ const union sockaddr_union *, const uint8_t *,
+ struct rm_priotracker *);
+static int geneve_ftable_learn(struct geneve_softc *,
+ const struct sockaddr *, const uint8_t *);
+
+static struct gnv_ftable_entry *
+ geneve_ftable_entry_alloc(void);
+static void geneve_ftable_entry_free(struct gnv_ftable_entry *);
+static void geneve_ftable_entry_init(struct geneve_softc *,
+ struct gnv_ftable_entry *, const uint8_t *,
+ const struct sockaddr *, uint32_t);
+static void geneve_ftable_entry_destroy(struct geneve_softc *,
+ struct gnv_ftable_entry *);
+static int geneve_ftable_entry_insert(struct geneve_softc *,
+ struct gnv_ftable_entry *);
+static struct gnv_ftable_entry *
+ geneve_ftable_entry_lookup(struct geneve_softc *,
+ const uint8_t *);
+
+static struct geneve_socket *
+ geneve_socket_alloc(union sockaddr_union *laddr);
+static void geneve_socket_destroy(struct geneve_socket *);
+static void geneve_socket_release(struct geneve_socket *);
+static struct geneve_socket *
+ geneve_socket_lookup(union sockaddr_union *);
+static void geneve_socket_insert(struct geneve_socket *);
+static int geneve_socket_init(struct geneve_socket *, struct ifnet *);
+static int geneve_socket_bind(struct geneve_socket *, struct ifnet *);
+static int geneve_socket_create(struct ifnet *, int,
+ const union sockaddr_union *, struct geneve_socket **);
+static int geneve_socket_set_df(struct geneve_socket *, bool);
+
+static struct geneve_socket *
+ geneve_socket_mc_lookup(const union sockaddr_union *);
+static int geneve_sockaddr_mc_info_match(
+ const struct geneve_socket_mc_info *,
+ const union sockaddr_union *,
+ const union sockaddr_union *, int);
+static int geneve_socket_mc_join_group(struct geneve_socket *,
+ const union sockaddr_union *, const union sockaddr_union *,
+ int *, union sockaddr_union *);
+static int geneve_socket_mc_leave_group(struct geneve_socket *,
+ const union sockaddr_union *,
+ const union sockaddr_union *, int);
+static int geneve_socket_mc_add_group(struct geneve_socket *,
+ const union sockaddr_union *,
+ const union sockaddr_union *, int, int *);
+static void geneve_socket_mc_release_group(struct geneve_socket *, int);
+
+static struct geneve_softc *
+ geneve_socket_lookup_softc_locked(struct geneve_socket *,
+ uint32_t);
+static struct geneve_softc *
+ geneve_socket_lookup_softc(struct geneve_socket *, uint32_t);
+static int geneve_socket_insert_softc(struct geneve_socket *,
+ struct geneve_softc *);
+static void geneve_socket_remove_softc(struct geneve_socket *,
+ struct geneve_softc *);
+
+static struct ifnet *
+ geneve_multicast_if_ref(struct geneve_softc *, uint32_t);
+static void geneve_free_multicast(struct geneve_softc *);
+static int geneve_setup_multicast_interface(struct geneve_softc *);
+
+static int geneve_setup_multicast(struct geneve_softc *);
+static int geneve_setup_socket(struct geneve_softc *);
+static void geneve_setup_interface_hdrlen(struct geneve_softc *);
+static int geneve_valid_init_config(struct geneve_softc *);
+static void geneve_init_complete(struct geneve_softc *);
+static void geneve_init(void *);
+static void geneve_release(struct geneve_softc *);
+static void geneve_teardown_wait(struct geneve_softc *);
+static void geneve_teardown_locked(struct geneve_softc *);
+static void geneve_teardown(struct geneve_softc *);
+static void geneve_timer(void *);
+
+static int geneve_flush_ftable(struct geneve_softc *, bool);
+static uint16_t geneve_get_local_port(struct geneve_softc *);
+static uint16_t geneve_get_remote_port(struct geneve_softc *);
+
+static int geneve_set_vni_nl(struct geneve_softc *, struct nl_pstate *,
+ uint32_t);
+static int geneve_set_local_addr_nl(struct geneve_softc *, struct nl_pstate *,
+ struct sockaddr *);
+static int geneve_set_remote_addr_nl(struct geneve_softc *, struct nl_pstate *,
+ struct sockaddr *);
+static int geneve_set_local_port_nl(struct geneve_softc *, struct nl_pstate *,
+ uint16_t);
+static int geneve_set_remote_port_nl(struct geneve_softc *, struct nl_pstate *,
+ uint16_t);
+static int geneve_set_port_range_nl(struct geneve_softc *, struct nl_pstate *,
+ struct ifla_geneve_port_range);
+static int geneve_set_df_nl(struct geneve_softc *, struct nl_pstate *,
+ enum ifla_geneve_df);
+static int geneve_set_ttl_nl(struct geneve_softc *, struct nl_pstate *,
+ uint8_t);
+static int geneve_set_ttl_inherit_nl(struct geneve_softc *, struct nl_pstate *,
+ bool);
+static int geneve_set_dscp_inherit_nl(struct geneve_softc *, struct nl_pstate *,
+ bool);
+static int geneve_set_collect_metadata_nl(struct geneve_softc *,
+ struct nl_pstate *, bool);
+static int geneve_set_learn_nl(struct geneve_softc *, struct nl_pstate *,
+ bool);
+static int geneve_set_ftable_max_nl(struct geneve_softc *, struct nl_pstate *,
+ uint32_t);
+static int geneve_set_ftable_timeout_nl(struct geneve_softc *,
+ struct nl_pstate *, uint32_t);
+static int geneve_set_mc_if_nl(struct geneve_softc *, struct nl_pstate *,
+ char *);
+static int geneve_flush_ftable_nl(struct geneve_softc *, struct nl_pstate *,
+ bool);
+static void geneve_get_local_addr_nl(struct geneve_softc *, struct nl_writer *);
+static void geneve_get_remote_addr_nl(struct geneve_softc *, struct nl_writer *);
+
+static int geneve_ioctl_ifflags(struct geneve_softc *);
+static int geneve_ioctl(struct ifnet *, u_long, caddr_t);
+
+static uint16_t geneve_pick_source_port(struct geneve_softc *, struct mbuf *);
+static void geneve_encap_header(struct geneve_softc *, struct mbuf *,
+ int, uint16_t, uint16_t, uint16_t);
+static uint16_t geneve_get_ethertype(struct mbuf *);
+static int geneve_inherit_l3_hdr(struct mbuf *, struct geneve_softc *,
+ uint16_t, uint8_t *, uint8_t *, u_short *);
+#ifdef INET
+static int geneve_encap4(struct geneve_softc *,
+ const union sockaddr_union *, struct mbuf *);
+#endif
+#ifdef INET6
+static int geneve_encap6(struct geneve_softc *,
+ const union sockaddr_union *, struct mbuf *);
+#endif
+static int geneve_transmit(struct ifnet *, struct mbuf *);
+static void geneve_qflush(struct ifnet *);
+static int geneve_output(struct ifnet *, struct mbuf *,
+ const struct sockaddr *, struct route *);
+static uint32_t geneve_map_etype_to_af(uint32_t);
+static bool geneve_udp_input(struct mbuf *, int, struct inpcb *,
+ const struct sockaddr *, void *);
+static int geneve_input_ether(struct geneve_softc *, struct mbuf **,
+ const struct sockaddr *, struct geneve_pkt_info *);
+static int geneve_input_inherit(struct geneve_softc *,
+ struct mbuf **, int, struct geneve_pkt_info *);
+static int geneve_next_option(struct geneve_socket *, struct genevehdr *,
+ struct mbuf **);
+static void geneve_input_csum(struct mbuf *m, struct ifnet *ifp,
+ counter_u64_t rxcsum);
+
+static void geneve_stats_alloc(struct geneve_softc *);
+static void geneve_stats_free(struct geneve_softc *);
+static void geneve_set_default_config(struct geneve_softc *);
+static int geneve_set_reqcap(struct geneve_softc *, struct ifnet *, int,
+ int);
+static void geneve_set_hwcaps(struct geneve_softc *);
+static int geneve_clone_create(struct if_clone *, char *, size_t,
+ struct ifc_data *, struct ifnet **);
+static int geneve_clone_destroy(struct if_clone *, struct ifnet *,
+ uint32_t);
+static int geneve_clone_create_nl(struct if_clone *, char *, size_t,
+ struct ifc_data_nl *);
+static int geneve_clone_modify_nl(struct ifnet *, struct ifc_data_nl *);
+static void geneve_clone_dump_nl(struct ifnet *, struct nl_writer *);
+
+static uint32_t geneve_mac_hash(struct geneve_softc *, const uint8_t *);
+static int geneve_media_change(struct ifnet *);
+static void geneve_media_status(struct ifnet *, struct ifmediareq *);
+
+static int geneve_sockaddr_cmp(const union sockaddr_union *,
+ const struct sockaddr *);
+static void geneve_sockaddr_copy(union sockaddr_union *,
+ const struct sockaddr *);
+static int geneve_sockaddr_in_equal(const union sockaddr_union *,
+ const struct sockaddr *);
+static void geneve_sockaddr_in_copy(union sockaddr_union *,
+ const struct sockaddr *);
+static int geneve_sockaddr_supported(const union sockaddr_union *, int);
+static int geneve_sockaddr_in_any(const union sockaddr_union *);
+
+static int geneve_can_change_config(struct geneve_softc *);
+static int geneve_check_proto(uint16_t);
+static int geneve_check_multicast_addr(const union sockaddr_union *);
+static int geneve_check_sockaddr(const union sockaddr_union *, const int);
+
+static int geneve_prison_remove(void *, void *);
+static void vnet_geneve_load(void);
+static void vnet_geneve_unload(void);
+static void geneve_module_init(void);
+static void geneve_module_deinit(void);
+static int geneve_modevent(module_t, int, void *);
+
+
+static const char geneve_name[] = "geneve";
+static MALLOC_DEFINE(M_GENEVE, geneve_name,
+ "Generic Network Virtualization Encapsulation Interface");
+#define MTAG_GENEVE_LOOP 0x93d66dc0 /* geneve mtag */
+
+VNET_DEFINE_STATIC(struct if_clone *, geneve_cloner);
+#define V_geneve_cloner VNET(geneve_cloner)
+
+static struct mtx geneve_list_mtx;
+#define GENEVE_LIST_LOCK() mtx_lock(&geneve_list_mtx)
+#define GENEVE_LIST_UNLOCK() mtx_unlock(&geneve_list_mtx)
+
+static LIST_HEAD(, geneve_socket) geneve_socket_list = LIST_HEAD_INITIALIZER(geneve_socket_list);
+
+/* Default maximum number of addresses in the forwarding table. */
+#define GENEVE_FTABLE_MAX 2000
+
+/* Timeout (in seconds) of addresses learned in the forwarding table. */
+#define GENEVE_FTABLE_TIMEOUT (20 * 60)
+
+/* Maximum timeout (in seconds) of addresses learned in the forwarding table. */
+#define GENEVE_FTABLE_MAX_TIMEOUT (60 * 60 * 24)
+
+/* Number of seconds between pruning attempts of the forwarding table. */
+#define GENEVE_FTABLE_PRUNE (5 * 60)
+
+static int geneve_ftable_prune_period = GENEVE_FTABLE_PRUNE;
+
+#define _OUT(_field) offsetof(struct nl_parsed_geneve, _field)
+static const struct nlattr_parser nla_p_geneve_create[] = {
+ { .type = IFLA_GENEVE_PROTOCOL, .off = _OUT(ifla_proto), .cb = nlattr_get_uint16 },
+};
+#undef _OUT
+NL_DECLARE_ATTR_PARSER(geneve_create_parser, nla_p_geneve_create);
+
+#define _OUT(_field) offsetof(struct nl_parsed_geneve, _field)
+static const struct nlattr_parser nla_p_geneve[] = {
+ { .type = IFLA_GENEVE_ID, .off = _OUT(ifla_vni), .cb = nlattr_get_uint32 },
+ { .type = IFLA_GENEVE_PROTOCOL, .off = _OUT(ifla_proto), .cb = nlattr_get_uint16 },
+ { .type = IFLA_GENEVE_LOCAL, .off = _OUT(ifla_local), .cb = nlattr_get_ip },
+ { .type = IFLA_GENEVE_REMOTE, .off = _OUT(ifla_remote), .cb = nlattr_get_ip },
+ { .type = IFLA_GENEVE_LOCAL_PORT, .off = _OUT(ifla_local_port), .cb = nlattr_get_uint16 },
+ { .type = IFLA_GENEVE_PORT, .off = _OUT(ifla_remote_port), .cb = nlattr_get_uint16 },
+ { .type = IFLA_GENEVE_PORT_RANGE, .off = _OUT(ifla_port_range),
+ .arg = (void *)sizeof(struct ifla_geneve_port_range), .cb = nlattr_get_bytes },
+ { .type = IFLA_GENEVE_DF, .off = _OUT(ifla_df), .cb = nlattr_get_uint8 },
+ { .type = IFLA_GENEVE_TTL, .off = _OUT(ifla_ttl), .cb = nlattr_get_uint8 },
+ { .type = IFLA_GENEVE_TTL_INHERIT, .off = _OUT(ifla_ttl_inherit), .cb = nlattr_get_bool },
+ { .type = IFLA_GENEVE_DSCP_INHERIT, .off = _OUT(ifla_dscp_inherit), .cb = nlattr_get_bool },
+ { .type = IFLA_GENEVE_COLLECT_METADATA, .off = _OUT(ifla_external), .cb = nlattr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_LEARN, .off = _OUT(ifla_ftable_learn), .cb = nlattr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_FLUSH, .off = _OUT(ifla_ftable_flush), .cb = nlattr_get_bool },
+ { .type = IFLA_GENEVE_FTABLE_MAX, .off = _OUT(ifla_ftable_max), .cb = nlattr_get_uint32 },
+ { .type = IFLA_GENEVE_FTABLE_TIMEOUT, .off = _OUT(ifla_ftable_timeout), .cb = nlattr_get_uint32 },
+ { .type = IFLA_GENEVE_MC_IFNAME, .off = _OUT(ifla_mc_ifname), .cb = nlattr_get_string },
+};
+#undef _OUT
+NL_DECLARE_ATTR_PARSER(geneve_modify_parser, nla_p_geneve);
+
+static const struct nlhdr_parser *all_parsers[] = {
+ &geneve_create_parser, &geneve_modify_parser,
+};
+
+static int
+geneve_ftable_addr_cmp(const uint8_t *a, const uint8_t *b)
+{
+ int i, d;
+
+ for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++)
+ d = (int)a[i] - (int)b[i];
+
+ return (d);
+}
+
+static void
+geneve_ftable_init(struct geneve_softc *sc)
+{
+ int i;
+
+ sc->gnv_ftable = malloc(sizeof(struct geneve_ftable_head) *
+ GENEVE_SC_FTABLE_SIZE, M_GENEVE, M_ZERO | M_WAITOK);
+
+ for (i = 0; i < GENEVE_SC_FTABLE_SIZE; i++)
+ LIST_INIT(&sc->gnv_ftable[i]);
+ sc->gnv_ftable_hash_key = arc4random();
+}
+
+static void
+geneve_ftable_fini(struct geneve_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < GENEVE_SC_FTABLE_SIZE; i++) {
+ KASSERT(LIST_EMPTY(&sc->gnv_ftable[i]),
+ ("%s: geneve %p ftable[%d] not empty", __func__, sc, i));
+ }
+ MPASS(sc->gnv_ftable_cnt == 0);
+
+ free(sc->gnv_ftable, M_GENEVE);
+ sc->gnv_ftable = NULL;
+}
+
+static void
+geneve_ftable_flush(struct geneve_softc *sc, int all)
+{
+ struct gnv_ftable_entry *fe, *tfe;
+
+ for (int i = 0; i < GENEVE_SC_FTABLE_SIZE; i++) {
+ LIST_FOREACH_SAFE(fe, &sc->gnv_ftable[i], gnvfe_hash, tfe) {
+ if (all || GENEVE_FE_IS_DYNAMIC(fe))
+ geneve_ftable_entry_destroy(sc, fe);
+ }
+ }
+}
+
+static void
+geneve_ftable_expire(struct geneve_softc *sc)
+{
+ struct gnv_ftable_entry *fe, *tfe;
+
+ GENEVE_LOCK_WASSERT(sc);
+
+ for (int i = 0; i < GENEVE_SC_FTABLE_SIZE; i++) {
+ LIST_FOREACH_SAFE(fe, &sc->gnv_ftable[i], gnvfe_hash, tfe) {
+ if (GENEVE_FE_IS_DYNAMIC(fe) &&
+ time_uptime >= fe->gnvfe_expire)
+ geneve_ftable_entry_destroy(sc, fe);
+ }
+ }
+}
+
+static int
+geneve_ftable_update_locked(struct geneve_softc *sc,
+ const union sockaddr_union *unsa, const uint8_t *mac,
+ struct rm_priotracker *tracker)
+{
+ struct gnv_ftable_entry *fe;
+ int error;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+again:
+ /*
+ * A forwarding entry for this MAC address might already exist. If
+ * so, update it, otherwise create a new one. We may have to upgrade
+ * the lock if we have to change or create an entry.
+ */
+ fe = geneve_ftable_entry_lookup(sc, mac);
+ if (fe != NULL) {
+ fe->gnvfe_expire = time_uptime + sc->gnv_ftable_timeout;
+
+ if (!GENEVE_FE_IS_DYNAMIC(fe) ||
+ geneve_sockaddr_in_equal(&fe->gnvfe_raddr, &unsa->sa))
+ return (0);
+ if (!GENEVE_LOCK_WOWNED(sc)) {
+ GENEVE_RUNLOCK(sc, tracker);
+ GENEVE_WLOCK(sc);
+ sc->gnv_stats.ftable_lock_upgrade_failed++;
+ goto again;
+ }
+ geneve_sockaddr_in_copy(&fe->gnvfe_raddr, &unsa->sa);
+ return (0);
+ }
+
+ if (!GENEVE_LOCK_WOWNED(sc)) {
+ GENEVE_RUNLOCK(sc, tracker);
+ GENEVE_WLOCK(sc);
+ sc->gnv_stats.ftable_lock_upgrade_failed++;
+ goto again;
+ }
+
+ if (sc->gnv_ftable_cnt >= sc->gnv_ftable_max) {
+ sc->gnv_stats.ftable_nospace++;
+ return (ENOSPC);
+ }
+
+ fe = geneve_ftable_entry_alloc();
+ if (fe == NULL)
+ return (ENOMEM);
+
+ geneve_ftable_entry_init(sc, fe, mac, &unsa->sa, GENEVE_FE_FLAG_DYNAMIC);
+
+ /* The prior lookup failed, so the insert should not. */
+ error = geneve_ftable_entry_insert(sc, fe);
+ MPASS(error == 0);
+
+ return (error);
+}
+
+static int
+geneve_ftable_learn(struct geneve_softc *sc, const struct sockaddr *sa,
+ const uint8_t *mac)
+{
+ struct rm_priotracker tracker;
+ union sockaddr_union unsa;
+ int error;
+
+ /*
+ * The source port may be randomly selected by the remote host, so
+ * use the port of the default destination address.
+ */
+ geneve_sockaddr_copy(&unsa, sa);
+ unsa.sin.sin_port = sc->gnv_dst_addr.sin.sin_port;
+
+#ifdef INET6
+ if (unsa.sa.sa_family == AF_INET6) {
+ error = sa6_embedscope(&unsa.sin6, V_ip6_use_defzone);
+ if (error)
+ return (error);
+ }
+#endif
+
+ GENEVE_RLOCK(sc, &tracker);
+ error = geneve_ftable_update_locked(sc, &unsa, mac, &tracker);
+ GENEVE_UNLOCK(sc, &tracker);
+
+ return (error);
+}
+
+static struct gnv_ftable_entry *
+geneve_ftable_entry_alloc(void)
+{
+ struct gnv_ftable_entry *fe;
+
+ fe = malloc(sizeof(*fe), M_GENEVE, M_ZERO | M_NOWAIT);
+
+ return (fe);
+}
+
+static void
+geneve_ftable_entry_free(struct gnv_ftable_entry *fe)
+{
+
+ free(fe, M_GENEVE);
+}
+
+static void
+geneve_ftable_entry_init(struct geneve_softc *sc, struct gnv_ftable_entry *fe,
+ const uint8_t *mac, const struct sockaddr *sa, uint32_t flags)
+{
+
+ fe->gnvfe_flags = flags;
+ fe->gnvfe_expire = time_uptime + sc->gnv_ftable_timeout;
+ memcpy(fe->gnvfe_mac, mac, ETHER_ADDR_LEN);
+ geneve_sockaddr_copy(&fe->gnvfe_raddr, sa);
+}
+
+static void
+geneve_ftable_entry_destroy(struct geneve_softc *sc,
+ struct gnv_ftable_entry *fe)
+{
+
+ sc->gnv_ftable_cnt--;
+ LIST_REMOVE(fe, gnvfe_hash);
+ geneve_ftable_entry_free(fe);
+}
+
+static int
+geneve_ftable_entry_insert(struct geneve_softc *sc,
+ struct gnv_ftable_entry *fe)
+{
+ struct gnv_ftable_entry *lfe;
+ uint32_t hash;
+ int dir;
+
+ GENEVE_LOCK_WASSERT(sc);
+ hash = GENEVE_SC_FTABLE_HASH(sc, fe->gnvfe_mac);
+
+ lfe = LIST_FIRST(&sc->gnv_ftable[hash]);
+ if (lfe == NULL) {
+ LIST_INSERT_HEAD(&sc->gnv_ftable[hash], fe, gnvfe_hash);
+ goto out;
+ }
+
+ do {
+ dir = geneve_ftable_addr_cmp(fe->gnvfe_mac, lfe->gnvfe_mac);
+ if (dir == 0)
+ return (EEXIST);
+ if (dir > 0) {
+ LIST_INSERT_BEFORE(lfe, fe, gnvfe_hash);
+ goto out;
+ } else if (LIST_NEXT(lfe, gnvfe_hash) == NULL) {
+ LIST_INSERT_AFTER(lfe, fe, gnvfe_hash);
+ goto out;
+ } else
+ lfe = LIST_NEXT(lfe, gnvfe_hash);
+ } while (lfe != NULL);
+
+out:
+ sc->gnv_ftable_cnt++;
+
+ return (0);
+}
+
+static struct gnv_ftable_entry *
+geneve_ftable_entry_lookup(struct geneve_softc *sc, const uint8_t *mac)
+{
+ struct gnv_ftable_entry *fe;
+ uint32_t hash;
+ int dir;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ hash = GENEVE_SC_FTABLE_HASH(sc, mac);
+ LIST_FOREACH(fe, &sc->gnv_ftable[hash], gnvfe_hash) {
+ dir = geneve_ftable_addr_cmp(mac, fe->gnvfe_mac);
+ if (dir == 0)
+ return (fe);
+ if (dir > 0)
+ break;
+ }
+
+ return (NULL);
+}
+
+static struct geneve_socket *
+geneve_socket_alloc(union sockaddr_union *laddr)
+{
+ struct geneve_socket *gnvso;
+
+ gnvso = malloc(sizeof(*gnvso), M_GENEVE, M_WAITOK | M_ZERO);
+ rm_init(&gnvso->gnvso_lock, "genevesorm");
+ refcount_init(&gnvso->gnvso_refcnt, 0);
+ for (int i = 0; i < GENEVE_SO_VNI_HASH_SIZE; i++)
+ LIST_INIT(&gnvso->gnvso_vni_hash[i]);
+ gnvso->gnvso_laddr = *laddr;
+
+ return (gnvso);
+}
+
+static void
+geneve_socket_destroy(struct geneve_socket *gnvso)
+{
+ struct socket *so;
+
+ so = gnvso->gnvso_sock;
+ if (so != NULL) {
+ gnvso->gnvso_sock = NULL;
+ soclose(so);
+ }
+
+ rm_destroy(&gnvso->gnvso_lock);
+ free(gnvso, M_GENEVE);
+}
+
+static void
+geneve_socket_release(struct geneve_socket *gnvso)
+{
+ int destroy;
+
+ GENEVE_LIST_LOCK();
+ destroy = GENEVE_SO_RELEASE(gnvso);
+ if (destroy != 0)
+ LIST_REMOVE(gnvso, gnvso_entry);
+ GENEVE_LIST_UNLOCK();
+
+ if (destroy != 0)
+ geneve_socket_destroy(gnvso);
+}
+
+static struct geneve_socket *
+geneve_socket_lookup(union sockaddr_union *unsa)
+{
+ struct geneve_socket *gnvso;
+
+ GENEVE_LIST_LOCK();
+ LIST_FOREACH(gnvso, &geneve_socket_list, gnvso_entry) {
+ if (geneve_sockaddr_cmp(&gnvso->gnvso_laddr, &unsa->sa) == 0) {
+ GENEVE_SO_ACQUIRE(gnvso);
+ break;
+ }
+ }
+ GENEVE_LIST_UNLOCK();
+
+ return (gnvso);
+}
+
+static void
+geneve_socket_insert(struct geneve_socket *gnvso)
+{
+
+ GENEVE_LIST_LOCK();
+ GENEVE_SO_ACQUIRE(gnvso);
+ LIST_INSERT_HEAD(&geneve_socket_list, gnvso, gnvso_entry);
+ GENEVE_LIST_UNLOCK();
+}
+
+static int
+geneve_socket_init(struct geneve_socket *gnvso, struct ifnet *ifp)
+{
+ struct thread *td;
+ int error;
+
+ td = curthread;
+ error = socreate(gnvso->gnvso_laddr.sa.sa_family, &gnvso->gnvso_sock,
+ SOCK_DGRAM, IPPROTO_UDP, td->td_ucred, td);
+ if (error) {
+ if_printf(ifp, "cannot create socket: %d\n", error);
+ return (error);
+ }
+
+ /*
+ * XXX: If Geneve traffic is shared with other UDP listeners on
+ * the same IP address, tunnel endpoints SHOULD implement a mechanism
+ * to ensure ICMP return traffic arising from network errors is
+ * directed to the correct listener. Unfortunately,
+ * udp_set_kernel_tunneling does not handle icmp errors from transit
+ * devices other than specified source.
+ */
+ error = udp_set_kernel_tunneling(gnvso->gnvso_sock,
+ geneve_udp_input, NULL, gnvso);
+ if (error)
+ if_printf(ifp, "cannot set tunneling function: %d\n", error);
+
+ return (error);
+}
+
+static int
+geneve_socket_bind(struct geneve_socket *gnvso, struct ifnet *ifp)
+{
+ union sockaddr_union laddr;
+ int error;
+
+ laddr = gnvso->gnvso_laddr;
+ error = sobind(gnvso->gnvso_sock, &laddr.sa, curthread);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static int
+geneve_socket_create(struct ifnet *ifp, int multicast,
+ const union sockaddr_union *unsa, struct geneve_socket **xgnvso)
+{
+ union sockaddr_union laddr;
+ struct geneve_socket *gnvso;
+ int error;
+
+ laddr = *unsa;
+
+ /*
+ * If this socket will be multicast, then only the local port
+ * must be specified when binding.
+ */
+ if (multicast != 0) {
+ switch (laddr.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ laddr.sin.sin_addr.s_addr = INADDR_ANY;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ laddr.sin6.sin6_addr = in6addr_any;
+ break;
+#endif
+ default:
+ return (EAFNOSUPPORT);
+ }
+ }
+ gnvso = geneve_socket_alloc(&laddr);
+ if (gnvso == NULL)
+ return (ENOMEM);
+
+ error = geneve_socket_init(gnvso, ifp);
+ if (error)
+ goto fail;
+
+ error = geneve_socket_bind(gnvso, ifp);
+ if (error)
+ goto fail;
+
+ /*
+ * There is a small window between the bind completing and
+ * inserting the socket, so that a concurrent create may fail.
+ * Let's not worry about that for now.
+ */
+ if_printf(ifp, "new geneve socket inserted to socket list\n");
+ geneve_socket_insert(gnvso);
+ *xgnvso = gnvso;
+
+ return (0);
+
+fail:
+ if_printf(ifp, "can't create new socket (error: %d)\n", error);
+ geneve_socket_destroy(gnvso);
+
+ return (error);
+}
+
+static struct geneve_socket *
+geneve_socket_mc_lookup(const union sockaddr_union *unsa)
+{
+ union sockaddr_union laddr;
+
+ laddr = *unsa;
+
+ switch (laddr.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ laddr.sin.sin_addr.s_addr = INADDR_ANY;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ laddr.sin6.sin6_addr = in6addr_any;
+ break;
+#endif
+ default:
+ return (NULL);
+ }
+
+ return (geneve_socket_lookup(&laddr));
+}
+
+static int
+geneve_sockaddr_mc_info_match(const struct geneve_socket_mc_info *mc,
+ const union sockaddr_union *group, const union sockaddr_union *local,
+ int ifidx)
+{
+
+ if (!geneve_sockaddr_in_any(local) &&
+ !geneve_sockaddr_in_equal(&mc->gnvsomc_saddr, &local->sa))
+ return (0);
+ if (!geneve_sockaddr_in_equal(&mc->gnvsomc_gaddr, &group->sa))
+ return (0);
+ if (ifidx != 0 && ifidx != mc->gnvsomc_ifidx)
+ return (0);
+
+ return (1);
+}
+
+static int
+geneve_socket_mc_join_group(struct geneve_socket *gnvso,
+ const union sockaddr_union *group, const union sockaddr_union *local,
+ int *ifidx, union sockaddr_union *source)
+{
+ struct sockopt sopt;
+ int error;
+
+ *source = *local;
+
+ if (group->sa.sa_family == AF_INET) {
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr = group->sin.sin_addr;
+ mreq.imr_interface = local->sin.sin_addr;
+
+ memset(&sopt, 0, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_level = IPPROTO_IP;
+ sopt.sopt_name = IP_ADD_MEMBERSHIP;
+ sopt.sopt_val = &mreq;
+ sopt.sopt_valsize = sizeof(mreq);
+ error = sosetopt(gnvso->gnvso_sock, &sopt);
+ if (error)
+ return (error);
+
+ /*
+ * BMV: Ideally, there would be a formal way for us to get
+ * the local interface that was selected based on the
+ * imr_interface address. We could then update *ifidx so
+ * geneve_sockaddr_mc_info_match() would return a match for
+ * later creates that explicitly set the multicast interface.
+ *
+ * If we really need to, we can of course look in the INP's
+ * membership list:
+ * sotoinpcb(gnvso->gnvso_sock)->inp_moptions->
+ * imo_head[]->imf_inm->inm_ifp
+ * similarly to imo_match_group().
+ */
+ source->sin.sin_addr = local->sin.sin_addr;
+
+ } else if (group->sa.sa_family == AF_INET6) {
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = group->sin6.sin6_addr;
+ mreq.ipv6mr_interface = *ifidx;
+
+ memset(&sopt, 0, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+ sopt.sopt_level = IPPROTO_IPV6;
+ sopt.sopt_name = IPV6_JOIN_GROUP;
+ sopt.sopt_val = &mreq;
+ sopt.sopt_valsize = sizeof(mreq);
+ error = sosetopt(gnvso->gnvso_sock, &sopt);
+
+ /*
+ * BMV: As with IPv4, we would really like to know what
+ * interface in6p_lookup_mcast_ifp() selected.
+ */
+ } else
+ error = EAFNOSUPPORT;
+
+ return (error);
+}
+
+static int
+geneve_socket_mc_leave_group(struct geneve_socket *gnvso,
+ const union sockaddr_union *group, const union sockaddr_union *source,
+ int ifidx)
+{
+ struct sockopt sopt;
+ int error;
+
+ memset(&sopt, 0, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+
+ if (group->sa.sa_family == AF_INET) {
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr = group->sin.sin_addr;
+ mreq.imr_interface = source->sin.sin_addr;
+
+ sopt.sopt_level = IPPROTO_IP;
+ sopt.sopt_name = IP_DROP_MEMBERSHIP;
+ sopt.sopt_val = &mreq;
+ sopt.sopt_valsize = sizeof(mreq);
+ error = sosetopt(gnvso->gnvso_sock, &sopt);
+ } else if (group->sa.sa_family == AF_INET6) {
+ struct ipv6_mreq mreq;
+
+ mreq.ipv6mr_multiaddr = group->sin6.sin6_addr;
+ mreq.ipv6mr_interface = ifidx;
+
+ sopt.sopt_level = IPPROTO_IPV6;
+ sopt.sopt_name = IPV6_LEAVE_GROUP;
+ sopt.sopt_val = &mreq;
+ sopt.sopt_valsize = sizeof(mreq);
+ error = sosetopt(gnvso->gnvso_sock, &sopt);
+ } else
+ error = EAFNOSUPPORT;
+
+ return (error);
+}
+
+static int
+geneve_socket_mc_add_group(struct geneve_socket *gnvso,
+ const union sockaddr_union *group, const union sockaddr_union *local,
+ int ifidx, int *idx)
+{
+ union sockaddr_union source;
+ struct geneve_socket_mc_info *mc;
+ int i, empty, error;
+
+ /*
+ * Within a socket, the same multicast group may be used by multiple
+ * interfaces, each with a different network identifier. But a socket
+ * may only join a multicast group once, so keep track of the users
+ * here.
+ */
+
+ GENEVE_SO_WLOCK(gnvso);
+ for (empty = 0, i = 0; i < GENEVE_SO_MC_MAX_GROUPS; i++) {
+ mc = &gnvso->gnvso_mc[i];
+
+ if (mc->gnvsomc_gaddr.sa.sa_family == AF_UNSPEC) {
+ empty++;
+ continue;
+ }
+ if (geneve_sockaddr_mc_info_match(mc, group, local, ifidx))
+ goto out;
+ }
+ GENEVE_SO_WUNLOCK(gnvso);
+
+ if (empty == 0)
+ return (ENOSPC);
+
+ error = geneve_socket_mc_join_group(gnvso, group, local, &ifidx, &source);
+ if (error)
+ return (error);
+
+ GENEVE_SO_WLOCK(gnvso);
+ for (i = 0; i < GENEVE_SO_MC_MAX_GROUPS; i++) {
+ mc = &gnvso->gnvso_mc[i];
+
+ if (mc->gnvsomc_gaddr.sa.sa_family == AF_UNSPEC) {
+ geneve_sockaddr_copy(&mc->gnvsomc_gaddr, &group->sa);
+ geneve_sockaddr_copy(&mc->gnvsomc_saddr, &source.sa);
+ mc->gnvsomc_ifidx = ifidx;
+ goto out;
+ }
+ }
+ GENEVE_SO_WUNLOCK(gnvso);
+
+ error = geneve_socket_mc_leave_group(gnvso, group, &source, ifidx);
+ MPASS(error == 0);
+
+ return (ENOSPC);
+
+out:
+ mc->gnvsomc_users++;
+ GENEVE_SO_WUNLOCK(gnvso);
+ *idx = i;
+
+ return (0);
+}
+
+static void
+geneve_socket_mc_release_group(struct geneve_socket *vso, int idx)
+{
+ union sockaddr_union group, source;
+ struct geneve_socket_mc_info *mc;
+ int ifidx, leave;
+
+ KASSERT(idx >= 0 && idx < GENEVE_SO_MC_MAX_GROUPS,
+ ("%s: vso %p idx %d out of bounds", __func__, vso, idx));
+
+ leave = 0;
+ mc = &vso->gnvso_mc[idx];
+
+ GENEVE_SO_WLOCK(vso);
+ mc->gnvsomc_users--;
+ if (mc->gnvsomc_users == 0) {
+ group = mc->gnvsomc_gaddr;
+ source = mc->gnvsomc_saddr;
+ ifidx = mc->gnvsomc_ifidx;
+ memset(mc, 0, sizeof(*mc));
+ leave = 1;
+ }
+ GENEVE_SO_WUNLOCK(vso);
+
+ if (leave != 0) {
+ /*
+ * Our socket's membership in this group may have already
+ * been removed if we joined through an interface that's
+ * been detached.
+ */
+ geneve_socket_mc_leave_group(vso, &group, &source, ifidx);
+ }
+}
+
+static struct geneve_softc *
+geneve_socket_lookup_softc_locked(struct geneve_socket *gnvso, uint32_t vni)
+{
+ struct geneve_softc *sc;
+ uint32_t hash;
+
+ GENEVE_SO_LOCK_ASSERT(gnvso);
+ hash = GENEVE_SO_VNI_HASH(vni);
+
+ LIST_FOREACH(sc, &gnvso->gnvso_vni_hash[hash], gnv_entry) {
+ if (sc->gnv_vni == vni) {
+ GENEVE_ACQUIRE(sc);
+ break;
+ }
+ }
+
+ return (sc);
+}
+
+static struct geneve_softc *
+geneve_socket_lookup_softc(struct geneve_socket *gnvso, uint32_t vni)
+{
+ struct rm_priotracker tracker;
+ struct geneve_softc *sc;
+
+ GENEVE_SO_RLOCK(gnvso, &tracker);
+ sc = geneve_socket_lookup_softc_locked(gnvso, vni);
+ GENEVE_SO_RUNLOCK(gnvso, &tracker);
+
+ return (sc);
+}
+
+static int
+geneve_socket_insert_softc(struct geneve_socket *gnvso, struct geneve_softc *sc)
+{
+ struct geneve_softc *tsc;
+ uint32_t vni, hash;
+
+ vni = sc->gnv_vni;
+ hash = GENEVE_SO_VNI_HASH(vni);
+
+ GENEVE_SO_WLOCK(gnvso);
+ tsc = geneve_socket_lookup_softc_locked(gnvso, vni);
+ if (tsc != NULL) {
+ GENEVE_SO_WUNLOCK(gnvso);
+ geneve_release(tsc);
+ return (EEXIST);
+ }
+
+ GENEVE_ACQUIRE(sc);
+ LIST_INSERT_HEAD(&gnvso->gnvso_vni_hash[hash], sc, gnv_entry);
+ GENEVE_SO_WUNLOCK(gnvso);
+
+ return (0);
+}
+
+static void
+geneve_socket_remove_softc(struct geneve_socket *gnvso, struct geneve_softc *sc)
+{
+
+ GENEVE_SO_WLOCK(gnvso);
+ LIST_REMOVE(sc, gnv_entry);
+ GENEVE_SO_WUNLOCK(gnvso);
+
+ geneve_release(sc);
+}
+
+static struct ifnet *
+geneve_multicast_if_ref(struct geneve_softc *sc, uint32_t af)
+{
+ struct ifnet *ifp;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ ifp = NULL;
+ if (af == AF_INET && sc->gnv_im4o != NULL)
+ ifp = sc->gnv_im4o->imo_multicast_ifp;
+ else if (af == AF_INET6 && sc->gnv_im6o != NULL)
+ ifp = sc->gnv_im6o->im6o_multicast_ifp;
+
+ if (ifp != NULL)
+ if_ref(ifp);
+
+ return (ifp);
+}
+
+static void
+geneve_free_multicast(struct geneve_softc *sc)
+{
+
+ if (sc->gnv_mc_ifp != NULL) {
+ if_rele(sc->gnv_mc_ifp);
+ sc->gnv_mc_ifp = NULL;
+ sc->gnv_mc_ifindex = 0;
+ }
+
+ if (sc->gnv_im4o != NULL) {
+ free(sc->gnv_im4o, M_GENEVE);
+ sc->gnv_im4o = NULL;
+ }
+
+ if (sc->gnv_im6o != NULL) {
+ free(sc->gnv_im6o, M_GENEVE);
+ sc->gnv_im6o = NULL;
+ }
+}
+
+static int
+geneve_setup_multicast_interface(struct geneve_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = ifunit_ref(sc->gnv_mc_ifname);
+ if (ifp == NULL) {
+ if_printf(sc->gnv_ifp, "multicast interface %s does not exist\n",
+ sc->gnv_mc_ifname);
+ return (ENOENT);
+ }
+
+ if ((ifp->if_flags & IFF_MULTICAST) == 0) {
+ if_printf(sc->gnv_ifp, "interface %s does not support multicast\n",
+ sc->gnv_mc_ifname);
+ if_rele(ifp);
+ return (ENOTSUP);
+ }
+
+ sc->gnv_mc_ifp = ifp;
+ sc->gnv_mc_ifindex = ifp->if_index;
+
+ return (0);
+}
+
+static int
+geneve_setup_multicast(struct geneve_softc *sc)
+{
+ const union sockaddr_union *group;
+ int error;
+
+ group = &sc->gnv_dst_addr;
+ error = 0;
+
+ if (sc->gnv_mc_ifname[0] != '\0') {
+ error = geneve_setup_multicast_interface(sc);
+ if (error)
+ return (error);
+ }
+
+ /*
+ * Initialize an multicast options structure that is sufficiently
+ * populated for use in the respective IP output routine. This
+ * structure is typically stored in the socket, but our sockets
+ * may be shared among multiple interfaces.
+ */
+ if (group->sa.sa_family == AF_INET) {
+ sc->gnv_im4o = malloc(sizeof(struct ip_moptions), M_GENEVE,
+ M_ZERO | M_WAITOK);
+ sc->gnv_im4o->imo_multicast_ifp = sc->gnv_mc_ifp;
+ sc->gnv_im4o->imo_multicast_ttl = sc->gnv_ttl;
+ sc->gnv_im4o->imo_multicast_vif = -1;
+ } else if (group->sa.sa_family == AF_INET6) {
+ sc->gnv_im6o = malloc(sizeof(struct ip6_moptions), M_GENEVE,
+ M_ZERO | M_WAITOK);
+ sc->gnv_im6o->im6o_multicast_ifp = sc->gnv_mc_ifp;
+ sc->gnv_im6o->im6o_multicast_hlim = sc->gnv_ttl;
+ }
+
+ return (error);
+}
+
+static int
+geneve_setup_socket(struct geneve_softc *sc)
+{
+ struct geneve_socket *gnvso;
+ struct ifnet *ifp;
+ union sockaddr_union *saddr, *daddr;
+ int multicast, error;
+
+ gnvso = NULL;
+ ifp = sc->gnv_ifp;
+ saddr = &sc->gnv_src_addr;
+ daddr = &sc->gnv_dst_addr;
+ multicast = geneve_check_multicast_addr(daddr);
+ MPASS(multicast != EINVAL);
+ sc->gnv_so_mc_index = -1;
+
+ /* Try to create the socket. If that fails, attempt to use an existing one. */
+ error = geneve_socket_create(ifp, multicast, saddr, &gnvso);
+ if (error) {
+ if (multicast != 0)
+ gnvso = geneve_socket_mc_lookup(saddr);
+ else
+ gnvso = geneve_socket_lookup(saddr);
+
+ if (gnvso == NULL) {
+ if_printf(ifp, "can't find existing socket\n");
+ goto out;
+ }
+ }
+
+ if (sc->gnv_df == IFLA_GENEVE_DF_SET) {
+ error = geneve_socket_set_df(gnvso, true);
+ if (error)
+ goto out;
+ }
+
+ if (multicast != 0) {
+ error = geneve_setup_multicast(sc);
+ if (error)
+ goto out;
+
+ error = geneve_socket_mc_add_group(gnvso, daddr, saddr,
+ sc->gnv_mc_ifindex, &sc->gnv_so_mc_index);
+ if (error)
+ goto out;
+ }
+
+ sc->gnv_sock = gnvso;
+ error = geneve_socket_insert_softc(gnvso, sc);
+ if (error) {
+ sc->gnv_sock = NULL;
+ if_printf(ifp, "network identifier %d already exists\n", sc->gnv_vni);
+ goto out;
+ }
+
+ return (0);
+
+out:
+ if (gnvso != NULL) {
+ if (sc->gnv_so_mc_index != -1) {
+ geneve_socket_mc_release_group(gnvso, sc->gnv_so_mc_index);
+ sc->gnv_so_mc_index = -1;
+ }
+ if (multicast != 0)
+ geneve_free_multicast(sc);
+ geneve_socket_release(gnvso);
+ }
+
+ return (error);
+}
+
+static void
+geneve_setup_interface_hdrlen(struct geneve_softc *sc)
+{
+ struct ifnet *ifp;
+
+ GENEVE_LOCK_WASSERT(sc);
+
+ ifp = sc->gnv_ifp;
+ ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct geneveudphdr);
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ ifp->if_hdrlen += ETHER_HDR_LEN;
+
+ if (sc->gnv_dst_addr.sa.sa_family == AF_INET)
+ ifp->if_hdrlen += sizeof(struct ip);
+ else
+ ifp->if_hdrlen += sizeof(struct ip6_hdr);
+
+ if ((sc->gnv_flags & GENEVE_FLAG_USER_MTU) == 0)
+ ifp->if_mtu = ETHERMTU - ifp->if_hdrlen;
+}
+
+static int
+geneve_socket_set_df(struct geneve_socket *gnvso, bool df)
+{
+ struct sockopt sopt;
+ int optval;
+
+ memset(&sopt, 0, sizeof(sopt));
+ sopt.sopt_dir = SOPT_SET;
+
+ switch (gnvso->gnvso_laddr.sa.sa_family) {
+ case AF_INET:
+ sopt.sopt_level = IPPROTO_IP;
+ sopt.sopt_name = IP_DONTFRAG;
+ break;
+
+ case AF_INET6:
+ sopt.sopt_level = IPPROTO_IPV6;
+ sopt.sopt_name = IPV6_DONTFRAG;
+ break;
+
+ default:
+ return (EAFNOSUPPORT);
+ }
+
+ optval = df ? 1 : 0;
+ sopt.sopt_val = &optval;
+ sopt.sopt_valsize = sizeof(optval);
+
+ return (sosetopt(gnvso->gnvso_sock, &sopt));
+}
+
+static int
+geneve_valid_init_config(struct geneve_softc *sc)
+{
+ const char *reason;
+
+ if (sc->gnv_vni >= GENEVE_VNI_MAX) {
+ if_printf(sc->gnv_ifp, "%u", sc->gnv_vni);
+ reason = "invalid virtual network identifier specified";
+ goto fail;
+ }
+
+ if (geneve_sockaddr_supported(&sc->gnv_src_addr, 1) == 0) {
+ reason = "source address type is not supported";
+ goto fail;
+ }
+
+ if (geneve_sockaddr_supported(&sc->gnv_dst_addr, 0) == 0) {
+ reason = "destination address type is not supported";
+ goto fail;
+ }
+
+ if (geneve_sockaddr_in_any(&sc->gnv_dst_addr) != 0) {
+ reason = "no valid destination address specified";
+ goto fail;
+ }
+
+ if (geneve_check_multicast_addr(&sc->gnv_dst_addr) == 0 &&
+ sc->gnv_mc_ifname[0] != '\0') {
+ reason = "can only specify interface with a group address";
+ goto fail;
+ }
+
+ if (geneve_sockaddr_in_any(&sc->gnv_src_addr) == 0) {
+ if (&sc->gnv_src_addr.sa.sa_family ==
+ &sc->gnv_dst_addr.sa.sa_family) {
+ reason = "source and destination address must both be either IPv4 or IPv6";
+ goto fail;
+ }
+ }
+
+ if (sc->gnv_src_addr.sin.sin_port == 0) {
+ reason = "local port not specified";
+ goto fail;
+ }
+
+ if (sc->gnv_dst_addr.sin.sin_port == 0) {
+ reason = "remote port not specified";
+ goto fail;
+ }
+
+ return (0);
+
+fail:
+ if_printf(sc->gnv_ifp, "cannot initialize interface: %s\n", reason);
+ return (EINVAL);
+}
+
+static void
+geneve_init_complete(struct geneve_softc *sc)
+{
+
+ GENEVE_WLOCK(sc);
+ sc->gnv_flags |= GENEVE_FLAG_RUNNING;
+ sc->gnv_flags &= ~GENEVE_FLAG_INIT;
+ wakeup(sc);
+ GENEVE_WUNLOCK(sc);
+}
+
+static void
+geneve_init(void *xsc)
+{
+ static const uint8_t empty_mac[ETHER_ADDR_LEN];
+ struct geneve_softc *sc;
+ struct ifnet *ifp;
+
+ sc = xsc;
+ sx_xlock(&geneve_sx);
+ GENEVE_WLOCK(sc);
+ ifp = sc->gnv_ifp;
+ if (sc->gnv_flags & GENEVE_FLAG_RUNNING) {
+ GENEVE_WUNLOCK(sc);
+ sx_xunlock(&geneve_sx);
+ return;
+ }
+ sc->gnv_flags |= GENEVE_FLAG_INIT;
+ GENEVE_WUNLOCK(sc);
+
+ if (geneve_valid_init_config(sc) != 0)
+ goto out;
+
+ if (geneve_setup_socket(sc) != 0)
+ goto out;
+
+ /* Initialize the default forwarding entry. */
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER) {
+ geneve_ftable_entry_init(sc, &sc->gnv_default_fe, empty_mac,
+ &sc->gnv_dst_addr.sa, GENEVE_FE_FLAG_STATIC);
+
+ GENEVE_WLOCK(sc);
+ callout_reset(&sc->gnv_callout, geneve_ftable_prune_period * hz,
+ geneve_timer, sc);
+ GENEVE_WUNLOCK(sc);
+ }
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ if_link_state_change(ifp, LINK_STATE_UP);
+
+out:
+ geneve_init_complete(sc);
+ sx_xunlock(&geneve_sx);
+}
+
+static void
+geneve_release(struct geneve_softc *sc)
+{
+
+ /*
+ * The softc may be destroyed as soon as we release our reference,
+ * so we cannot serialize the wakeup with the softc lock. We use a
+ * timeout in our sleeps so a missed wakeup is unfortunate but not fatal.
+ */
+ if (GENEVE_RELEASE(sc) != 0)
+ wakeup(sc);
+}
+
+static void
+geneve_teardown_wait(struct geneve_softc *sc)
+{
+
+ GENEVE_LOCK_WASSERT(sc);
+ while (sc->gnv_flags & GENEVE_FLAG_TEARDOWN)
+ rm_sleep(sc, &sc->gnv_lock, 0, "gnvtrn", hz);
+}
+
+static void
+geneve_teardown_locked(struct geneve_softc *sc)
+{
+ struct ifnet *ifp;
+ struct geneve_socket *gnvso;
+
+ sx_assert(&geneve_sx, SA_XLOCKED);
+ GENEVE_LOCK_WASSERT(sc);
+ MPASS(sc->gnv_flags & GENEVE_FLAG_TEARDOWN);
+
+ ifp = sc->gnv_ifp;
+ ifp->if_flags &= ~IFF_UP;
+ sc->gnv_flags &= ~GENEVE_FLAG_RUNNING;
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ callout_stop(&sc->gnv_callout);
+ gnvso = sc->gnv_sock;
+ sc->gnv_sock = NULL;
+
+ GENEVE_WUNLOCK(sc);
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+
+ if (gnvso != NULL) {
+ geneve_socket_remove_softc(gnvso, sc);
+
+ if (sc->gnv_so_mc_index != -1) {
+ geneve_socket_mc_release_group(gnvso, sc->gnv_so_mc_index);
+ sc->gnv_so_mc_index = -1;
+ }
+ }
+
+ GENEVE_WLOCK(sc);
+ while (sc->gnv_refcnt != 0)
+ rm_sleep(sc, &sc->gnv_lock, 0, "gnvdrn", hz);
+ GENEVE_WUNLOCK(sc);
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ callout_drain(&sc->gnv_callout);
+
+ geneve_free_multicast(sc);
+ if (gnvso != NULL)
+ geneve_socket_release(gnvso);
+
+ GENEVE_WLOCK(sc);
+ sc->gnv_flags &= ~GENEVE_FLAG_TEARDOWN;
+ wakeup(sc);
+ GENEVE_WUNLOCK(sc);
+}
+
+static void
+geneve_teardown(struct geneve_softc *sc)
+{
+
+ sx_xlock(&geneve_sx);
+ GENEVE_WLOCK(sc);
+ if (sc->gnv_flags & GENEVE_FLAG_TEARDOWN) {
+ geneve_teardown_wait(sc);
+ GENEVE_WUNLOCK(sc);
+ sx_xunlock(&geneve_sx);
+ return;
+ }
+
+ sc->gnv_flags |= GENEVE_FLAG_TEARDOWN;
+ geneve_teardown_locked(sc);
+ sx_xunlock(&geneve_sx);
+}
+
+static void
+geneve_timer(void *xsc)
+{
+ struct geneve_softc *sc;
+
+ sc = xsc;
+ GENEVE_LOCK_WASSERT(sc);
+
+ geneve_ftable_expire(sc);
+ callout_schedule(&sc->gnv_callout, geneve_ftable_prune_period * hz);
+}
+
+static int
+geneve_ioctl_ifflags(struct geneve_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->gnv_ifp;
+
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ if ((sc->gnv_flags & GENEVE_FLAG_RUNNING) == 0)
+ geneve_init(sc);
+ } else {
+ if (sc->gnv_flags & GENEVE_FLAG_RUNNING)
+ geneve_teardown(sc);
+ }
+
+ return (0);
+}
+
+static int
+geneve_flush_ftable(struct geneve_softc *sc, bool flush)
+{
+
+ GENEVE_WLOCK(sc);
+ geneve_ftable_flush(sc, flush);
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static uint16_t
+geneve_get_local_port(struct geneve_softc *sc)
+{
+ uint16_t port = 0;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ switch (sc->gnv_src_addr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(sc->gnv_src_addr.sin.sin_port);
+ break;
+ case AF_INET6:
+ port = ntohs(sc->gnv_src_addr.sin6.sin6_port);
+ break;
+ }
+
+ return (port);
+}
+
+static uint16_t
+geneve_get_remote_port(struct geneve_softc *sc)
+{
+ uint16_t port = 0;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ switch (sc->gnv_dst_addr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(sc->gnv_dst_addr.sin.sin_port);
+ break;
+ case AF_INET6:
+ port = ntohs(sc->gnv_dst_addr.sin6.sin6_port);
+ break;
+ }
+
+ return (port);
+}
+
+/* Netlink Helpers */
+static int
+geneve_set_vni_nl(struct geneve_softc *sc, struct nl_pstate *npt, uint32_t vni)
+{
+ int error;
+
+ error = 0;
+ if (vni >= GENEVE_VNI_MAX) {
+ error = EINVAL;
+ goto ret;
+ }
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc))
+ sc->gnv_vni = vni;
+ else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "geneve vni is invalid: %u", vni);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_local_addr_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ struct sockaddr *sa)
+{
+ union sockaddr_union *unsa = (union sockaddr_union *)sa;
+ int error;
+
+ error = geneve_check_sockaddr(unsa, sa->sa_len);
+ if (error != 0)
+ goto ret;
+
+ error = geneve_check_multicast_addr(unsa);
+ if (error != 0)
+ goto ret;
+
+#ifdef INET6
+ if (unsa->sa.sa_family == AF_INET6) {
+ error = sa6_embedscope(&unsa->sin6, V_ip6_use_defzone);
+ if (error != 0)
+ goto ret;
+ }
+#endif
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc)) {
+ geneve_sockaddr_in_copy(&sc->gnv_src_addr, &unsa->sa);
+ geneve_set_hwcaps(sc);
+ } else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "local address is invalid.");
+
+ if (error == EAFNOSUPPORT)
+ nlmsg_report_err_msg(npt, "address family is not supported.");
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_remote_addr_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ struct sockaddr *sa)
+{
+ union sockaddr_union *unsa = (union sockaddr_union *)sa;
+ int error;
+
+ error = geneve_check_sockaddr(unsa, sa->sa_len);
+ if (error != 0)
+ goto ret;
+
+#ifdef INET6
+ if (unsa->sa.sa_family == AF_INET6) {
+ error = sa6_embedscope(&unsa->sin6, V_ip6_use_defzone);
+ if (error != 0)
+ goto ret;
+ }
+#endif
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc)) {
+ geneve_sockaddr_in_copy(&sc->gnv_dst_addr, &unsa->sa);
+ geneve_setup_interface_hdrlen(sc);
+ } else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "remote address is invalid.");
+
+ if (error == EAFNOSUPPORT)
+ nlmsg_report_err_msg(npt, "address family is not supported.");
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_local_port_nl(struct geneve_softc *sc, struct nl_pstate *npt, uint16_t port)
+{
+ int error;
+
+ error = 0;
+ if (port == 0 || port > UINT16_MAX) {
+ error = EINVAL;
+ goto ret;
+ }
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc) == 0) {
+ GENEVE_WUNLOCK(sc);
+ error = EBUSY;
+ goto ret;
+ }
+
+ switch (sc->gnv_src_addr.sa.sa_family) {
+ case AF_INET:
+ sc->gnv_src_addr.sin.sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sc->gnv_src_addr.sin6.sin6_port = htons(port);
+ break;
+ }
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "local port is invalid: %u", port);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_remote_port_nl(struct geneve_softc *sc, struct nl_pstate *npt, uint16_t port)
+{
+ int error;
+
+ error = 0;
+ if (port == 0 || port > UINT16_MAX) {
+ error = EINVAL;
+ goto ret;
+ }
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc) == 0) {
+ GENEVE_WUNLOCK(sc);
+ error = EBUSY;
+ goto ret;
+ }
+
+ switch (sc->gnv_dst_addr.sa.sa_family) {
+ case AF_INET:
+ sc->gnv_dst_addr.sin.sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sc->gnv_dst_addr.sin6.sin6_port = htons(port);
+ break;
+ }
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "remote port is invalid: %u", port);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_port_range_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ struct ifla_geneve_port_range port_range)
+{
+ int error;
+
+ error = 0;
+ if (port_range.low <= 0 || port_range.high > UINT16_MAX ||
+ port_range.high < port_range.low) {
+ error = EINVAL;
+ goto ret;
+ }
+
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc)) {
+ sc->gnv_min_port = port_range.low;
+ sc->gnv_max_port = port_range.high;
+ } else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ret:
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt, "port range is invalid: %u-%u",
+ port_range.low, port_range.high);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_df_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ enum ifla_geneve_df df)
+{
+ int error;
+
+ error = 0;
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc))
+ sc->gnv_df = df;
+ else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_set_ttl_nl(struct geneve_softc *sc, struct nl_pstate *npt __unused,
+ uint8_t ttl)
+{
+
+ GENEVE_WLOCK(sc);
+ sc->gnv_ttl = ttl;
+ if (sc->gnv_im4o != NULL)
+ sc->gnv_im4o->imo_multicast_ttl = sc->gnv_ttl;
+ if (sc->gnv_im6o != NULL)
+ sc->gnv_im6o->im6o_multicast_hlim = sc->gnv_ttl;
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static int
+geneve_set_ttl_inherit_nl(struct geneve_softc *sc,
+ struct nl_pstate *npt __unused, bool inherit)
+{
+
+ GENEVE_WLOCK(sc);
+ if (inherit)
+ sc->gnv_flags |= GENEVE_FLAG_TTL_INHERIT;
+ else
+ sc->gnv_flags &= ~GENEVE_FLAG_TTL_INHERIT;
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static int
+geneve_set_dscp_inherit_nl(struct geneve_softc *sc,
+ struct nl_pstate *npt __unused, bool inherit)
+{
+
+ GENEVE_WLOCK(sc);
+ if (inherit)
+ sc->gnv_flags |= GENEVE_FLAG_DSCP_INHERIT;
+ else
+ sc->gnv_flags &= ~GENEVE_FLAG_DSCP_INHERIT;
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static int
+geneve_set_collect_metadata_nl(struct geneve_softc *sc,
+ struct nl_pstate *npt __unused, bool external)
+{
+
+ GENEVE_WLOCK(sc);
+ if (external)
+ sc->gnv_flags |= GENEVE_FLAG_COLLECT_METADATA;
+ else
+ sc->gnv_flags &= ~GENEVE_FLAG_COLLECT_METADATA;
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static int
+geneve_set_learn_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ bool learn)
+{
+
+ GENEVE_WLOCK(sc);
+ if (learn)
+ sc->gnv_flags |= GENEVE_FLAG_LEARN;
+ else
+ sc->gnv_flags &= ~GENEVE_FLAG_LEARN;
+ GENEVE_WUNLOCK(sc);
+
+ return (0);
+}
+
+static int
+geneve_set_ftable_max_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ uint32_t max)
+{
+ int error;
+
+ error = 0;
+ GENEVE_WLOCK(sc);
+ if (max <= GENEVE_FTABLE_MAX)
+ sc->gnv_ftable_max = max;
+ else
+ error = EINVAL;
+ GENEVE_WUNLOCK(sc);
+
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt,
+ "maximum number of entries in the table can not be more than %u",
+ GENEVE_FTABLE_MAX);
+
+ return (error);
+}
+
+static int
+geneve_set_ftable_timeout_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ uint32_t timeout)
+{
+ int error;
+
+ error = 0;
+ GENEVE_WLOCK(sc);
+ if (timeout <= GENEVE_FTABLE_MAX_TIMEOUT)
+ sc->gnv_ftable_timeout = timeout;
+ else
+ error = EINVAL;
+ GENEVE_WUNLOCK(sc);
+
+ if (error == EINVAL)
+ nlmsg_report_err_msg(npt,
+ "maximum timeout for stale entries in the table can not be more than %u",
+ GENEVE_FTABLE_MAX_TIMEOUT);
+
+ return (error);
+}
+
+static int
+geneve_set_mc_if_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ char *ifname)
+{
+ int error;
+
+ error = 0;
+ GENEVE_WLOCK(sc);
+ if (geneve_can_change_config(sc)) {
+ strlcpy(sc->gnv_mc_ifname, ifname, IFNAMSIZ);
+ geneve_set_hwcaps(sc);
+ } else
+ error = EBUSY;
+ GENEVE_WUNLOCK(sc);
+
+ if (error == EBUSY)
+ nlmsg_report_err_msg(npt, "geneve interface is busy.");
+
+ return (error);
+}
+
+static int
+geneve_flush_ftable_nl(struct geneve_softc *sc, struct nl_pstate *npt,
+ bool flush)
+{
+
+ return (geneve_flush_ftable(sc, flush));
+}
+
+static void
+geneve_get_local_addr_nl(struct geneve_softc *sc, struct nl_writer *nw)
+{
+ struct sockaddr *sa;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ sa = &sc->gnv_src_addr.sa;
+ if (sa->sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ nlattr_add_in_addr(nw, IFLA_GENEVE_LOCAL, in4);
+ } else if (sa->sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ nlattr_add_in6_addr(nw, IFLA_GENEVE_LOCAL, in6);
+ }
+}
+
+static void
+geneve_get_remote_addr_nl(struct geneve_softc *sc, struct nl_writer *nw)
+{
+ struct sockaddr *sa;
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ sa = &sc->gnv_dst_addr.sa;
+ if (sa->sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ nlattr_add_in_addr(nw, IFLA_GENEVE_REMOTE, in4);
+ } else if (sa->sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ nlattr_add_in6_addr(nw, IFLA_GENEVE_REMOTE, in6);
+ }
+}
+
+static int
+geneve_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct rm_priotracker tracker;
+ struct geneve_softc *sc;
+ struct siocsifcapnv_driver_data *drv_ioctl_data, drv_ioctl_data_d;
+ struct ifreq *ifr;
+ int max, error;
+
+ CURVNET_ASSERT_SET();
+
+ error = 0;
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ switch (cmd) {
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ break;
+
+ case SIOCGDRVSPEC:
+ break;
+ case SIOCSDRVSPEC:
+ error = priv_check(curthread, PRIV_NET_GENEVE);
+ if (error)
+ return (error);
+ break;
+ }
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ error = geneve_ioctl_ifflags(sc);
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ error = ifmedia_ioctl(ifp, ifr, &sc->gnv_media, cmd);
+ else
+ error = EINVAL;
+ break;
+
+ case SIOCSIFMTU:
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ max = GENEVE_MAX_MTU;
+ else
+ max = GENEVE_MAX_L3MTU;
+
+ if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > max)
+ error = EINVAL;
+ else {
+ GENEVE_WLOCK(sc);
+ ifp->if_mtu = ifr->ifr_mtu;
+ sc->gnv_flags |= GENEVE_FLAG_USER_MTU;
+ GENEVE_WUNLOCK(sc);
+ }
+ break;
+
+ case SIOCGIFCAPNV:
+ break;
+ case SIOCSIFCAP:
+ drv_ioctl_data = &drv_ioctl_data_d;
+ drv_ioctl_data->reqcap = ifr->ifr_reqcap;
+ drv_ioctl_data->reqcap2 = if_getcapenable2(ifp);
+ drv_ioctl_data->nvcap = NULL;
+ /* FALLTHROUGH */
+ case SIOCSIFCAPNV:
+ if (cmd == SIOCSIFCAPNV)
+ drv_ioctl_data = (struct siocsifcapnv_driver_data *)data;
+
+ GENEVE_WLOCK(sc);
+ error = geneve_set_reqcap(sc, ifp, drv_ioctl_data->reqcap,
+ drv_ioctl_data->reqcap2);
+ if (error == 0)
+ geneve_set_hwcaps(sc);
+ GENEVE_WUNLOCK(sc);
+ break;
+
+ case SIOCGTUNFIB:
+ GENEVE_RLOCK(sc, &tracker);
+ ifr->ifr_fib = sc->gnv_fibnum;
+ GENEVE_RUNLOCK(sc, &tracker);
+ break;
+
+ case SIOCSTUNFIB:
+ if ((error = priv_check(curthread, PRIV_NET_GENEVE)) != 0)
+ break;
+
+ if (ifr->ifr_fib >= rt_numfibs)
+ error = EINVAL;
+ else {
+ GENEVE_WLOCK(sc);
+ sc->gnv_fibnum = ifr->ifr_fib;
+ GENEVE_WUNLOCK(sc);
+ }
+ break;
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ /* FALLTHROUGH */
+ case SIOCGIFADDR:
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+
+ default:
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ error = ether_ioctl(ifp, cmd, data);
+ else
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+static uint16_t
+geneve_pick_source_port(struct geneve_softc *sc, struct mbuf *m)
+{
+ int range;
+ uint32_t hash;
+
+ range = sc->gnv_max_port - sc->gnv_min_port + 1;
+
+ /* RFC 8926 Section 3.3-2.2.1 */
+ if (M_HASHTYPE_ISHASH(m))
+ hash = m->m_pkthdr.flowid;
+ else
+ hash = jenkins_hash(m->m_data, ETHER_HDR_LEN, sc->gnv_port_hash_key);
+
+ return (sc->gnv_min_port + (hash % range));
+}
+
+static void
+geneve_encap_header(struct geneve_softc *sc, struct mbuf *m, int ipoff,
+ uint16_t srcport, uint16_t dstport, uint16_t proto)
+{
+ struct geneveudphdr *hdr;
+ struct udphdr *udph;
+ struct genevehdr *gnvh;
+ int len;
+
+ len = m->m_pkthdr.len - ipoff;
+ MPASS(len >= sizeof(struct geneveudphdr));
+ hdr = mtodo(m, ipoff);
+
+ udph = &hdr->geneve_udp;
+ udph->uh_sport = srcport;
+ udph->uh_dport = dstport;
+ udph->uh_ulen = htons(len);
+ udph->uh_sum = 0;
+
+ gnvh = &hdr->geneve_hdr;
+ gnvh->geneve_ver = 0;
+ gnvh->geneve_optlen = 0;
+ gnvh->geneve_critical = 0;
+ gnvh->geneve_control = 0;
+ gnvh->geneve_flags = 0;
+ gnvh->geneve_proto = proto;
+ gnvh->geneve_vni = htonl(sc->gnv_vni << GENEVE_HDR_VNI_SHIFT);
+}
+
+/* Return the CSUM_INNER_* equivalent of CSUM_* caps. */
+static uint32_t
+csum_flags_to_inner_flags(uint32_t csum_flags_in, const uint32_t encap)
+{
+ uint32_t csum_flags = encap;
+ const uint32_t v4 = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP;
+
+ /*
+ * csum_flags can request either v4 or v6 offload but not both.
+ * tcp_output always sets CSUM_TSO (both CSUM_IP_TSO and CSUM_IP6_TSO)
+ * so those bits are no good to detect the IP version. Other bits are
+ * always set with CSUM_TSO and we use those to figure out the IP
+ * version.
+ */
+ if (csum_flags_in & v4) {
+ if (csum_flags_in & CSUM_IP)
+ csum_flags |= CSUM_INNER_IP;
+ if (csum_flags_in & CSUM_IP_UDP)
+ csum_flags |= CSUM_INNER_IP_UDP;
+ if (csum_flags_in & CSUM_IP_TCP)
+ csum_flags |= CSUM_INNER_IP_TCP;
+ if (csum_flags_in & CSUM_IP_TSO)
+ csum_flags |= CSUM_INNER_IP_TSO;
+ } else {
+#ifdef INVARIANTS
+ const uint32_t v6 = CSUM_IP6_UDP | CSUM_IP6_TCP;
+ MPASS((csum_flags_in & v6) != 0);
+#endif
+ if (csum_flags_in & CSUM_IP6_UDP)
+ csum_flags |= CSUM_INNER_IP6_UDP;
+ if (csum_flags_in & CSUM_IP6_TCP)
+ csum_flags |= CSUM_INNER_IP6_TCP;
+ if (csum_flags_in & CSUM_IP6_TSO)
+ csum_flags |= CSUM_INNER_IP6_TSO;
+ }
+
+ return (csum_flags);
+}
+
+static uint16_t
+geneve_get_ethertype(struct mbuf *m)
+{
+ struct ip *ip;
+ struct ip6_hdr *ip6;
+
+ /*
+ * We should pullup, but we're only interested in the first byte, so
+ * that'll always be contiguous.
+ */
+ ip = mtod(m, struct ip *);
+ if (ip->ip_v == IPVERSION)
+ return (ETHERTYPE_IP);
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ if ((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION)
+ return (ETHERTYPE_IPV6);
+
+ return (0);
+}
+
+/* RFC 8926 Section 4.4.2. DSCP, ECN, and TTL */
+static int
+geneve_inherit_l3_hdr(struct mbuf *m, struct geneve_softc *sc, uint16_t proto,
+ uint8_t *tos, uint8_t *ttl, u_short *ip_off)
+{
+ struct ether_header *eh;
+ struct ip *ip_inner, iphdr;
+ struct ip6_hdr *ip6_inner, ip6hdr;
+ int offset;
+
+ *tos = 0;
+ *ttl = sc->gnv_ttl;
+ if (sc->gnv_df == IFLA_GENEVE_DF_SET)
+ *ip_off = htons(IP_DF);
+ else
+ *ip_off = 0;
+
+ /* Set offset and address family if proto is ethernet */
+ if (proto == GENEVE_PROTO_ETHER) {
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type == htons(ETHERTYPE_IP)) {
+ if (m->m_pkthdr.len < ETHER_HDR_LEN + sizeof(struct ip)) {
+ m_freem(m);
+ return (EINVAL);
+ }
+ proto = ETHERTYPE_IP;
+ } else if (eh->ether_type == htons(ETHERTYPE_IPV6)) {
+ if (m->m_pkthdr.len < ETHER_HDR_LEN + sizeof(struct ip6_hdr)) {
+ m_freem(m);
+ return (EINVAL);
+ }
+ proto = ETHERTYPE_IPV6;
+ } else
+ return (0);
+
+ offset = ETHER_HDR_LEN;
+ } else
+ offset = 0;
+
+ switch (proto) {
+ case ETHERTYPE_IP:
+ if (__predict_false(m->m_len < offset + sizeof(struct ip))) {
+ m_copydata(m, offset, sizeof(struct ip), (caddr_t)&iphdr);
+ ip_inner = &iphdr;
+ } else
+ ip_inner = mtodo(m, offset);
+
+ *tos = ip_inner->ip_tos;
+ if (sc->gnv_flags & GENEVE_FLAG_TTL_INHERIT)
+ *ttl = ip_inner->ip_ttl;
+ if (sc->gnv_df == IFLA_GENEVE_DF_INHERIT)
+ *ip_off = ip_inner->ip_off;
+ break;
+
+ case ETHERTYPE_IPV6:
+ if (__predict_false(m->m_len < offset + sizeof(struct ip6_hdr))) {
+ m_copydata(m, offset, sizeof(struct ip6_hdr), (caddr_t)&ip6hdr);
+ ip6_inner = &ip6hdr;
+ } else
+ ip6_inner = mtodo(m, offset);
+
+ *tos = IPV6_TRAFFIC_CLASS(ip6_inner);
+ if (sc->gnv_flags & GENEVE_FLAG_TTL_INHERIT)
+ *ttl = ip6_inner->ip6_hlim;
+ break;
+ }
+
+ return (0);
+}
+
+#ifdef INET
+static int
+geneve_encap4(struct geneve_softc *sc, const union sockaddr_union *funsa,
+ struct mbuf *m)
+{
+ struct ifnet *ifp;
+ struct ip *ip;
+ struct in_addr srcaddr, dstaddr;
+ struct route route, *ro;
+ struct sockaddr_in *sin;
+ int plen, error;
+ uint32_t csum_flags;
+ uint16_t srcport, dstport, proto;
+ u_short ip_off;
+ uint8_t tos, ecn, ttl;
+ bool mcast;
+
+ NET_EPOCH_ASSERT();
+
+ ifp = sc->gnv_ifp;
+ srcaddr = sc->gnv_src_addr.sin.sin_addr;
+ srcport = htons(geneve_pick_source_port(sc, m));
+ dstaddr = funsa->sin.sin_addr;
+ dstport = funsa->sin.sin_port;
+ plen = m->m_pkthdr.len;
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ proto = sc->gnv_proto;
+ else
+ proto = geneve_get_ethertype(m);
+
+ error = geneve_inherit_l3_hdr(m, sc, proto, &tos, &ttl, &ip_off);
+ if (error) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (error);
+ }
+
+ M_PREPEND(m, sizeof(struct ip) + sizeof(struct geneveudphdr), M_NOWAIT);
+ if (m == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENOBUFS);
+ }
+ ip = mtod(m, struct ip *);
+
+ ecn = (tos & IPTOS_ECN_MASK);
+ ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &ecn);
+ if (sc->gnv_flags & GENEVE_FLAG_DSCP_INHERIT)
+ ip->ip_tos |= (tos & ~IPTOS_ECN_MASK);
+
+ ip->ip_len = htons(m->m_pkthdr.len);
+ ip->ip_off = ip_off;
+ ip->ip_ttl = ttl;
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_sum = 0;
+ ip->ip_src = srcaddr;
+ ip->ip_dst = dstaddr;
+
+ geneve_encap_header(sc, m, sizeof(struct ip), srcport, dstport, htons(proto));
+ mcast = (m->m_flags & (M_MCAST | M_BCAST));
+ m->m_flags &= ~(M_MCAST | M_BCAST);
+
+ m->m_pkthdr.csum_flags &= CSUM_FLAGS_TX;
+ if (m->m_pkthdr.csum_flags != 0) {
+ /*
+ * HW checksum (L3 and/or L4) or TSO has been requested.
+ * Look up the ifnet for the outbound route and verify that the
+ * outbound ifnet can perform the requested operation on the inner frame.
+ */
+ memset(&route, 0, sizeof(route));
+ ro = &route;
+ sin = (struct sockaddr_in *)&ro->ro_dst;
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ip->ip_dst;
+ ro->ro_nh = fib4_lookup(M_GETFIB(m), ip->ip_dst, 0, NHR_NONE, 0);
+ if (ro->ro_nh == NULL) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (EHOSTUNREACH);
+ }
+
+ csum_flags = csum_flags_to_inner_flags(m->m_pkthdr.csum_flags,
+ CSUM_ENCAP_GENEVE);
+ if ((csum_flags & ro->ro_nh->nh_ifp->if_hwassist) != csum_flags) {
+ if (ppsratecheck(&sc->err_time, &sc->err_pps, 1)) {
+ const struct ifnet *nh_ifp = ro->ro_nh->nh_ifp;
+
+ if_printf(ifp, "interface %s is missing hwcaps "
+ "0x%08x, csum_flags 0x%08x -> 0x%08x, "
+ "hwassist 0x%08x\n", nh_ifp->if_xname,
+ csum_flags & ~(uint32_t)nh_ifp->if_hwassist,
+ m->m_pkthdr.csum_flags, csum_flags,
+ (uint32_t)nh_ifp->if_hwassist);
+ }
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENXIO);
+ }
+ m->m_pkthdr.csum_flags = csum_flags;
+ if (csum_flags & (CSUM_INNER_IP | CSUM_INNER_IP_UDP |
+ CSUM_INNER_IP6_UDP | CSUM_INNER_IP_TCP | CSUM_INNER_IP6_TCP)) {
+ counter_u64_add(sc->gnv_stats.txcsum, 1);
+ if (csum_flags & CSUM_INNER_TSO)
+ counter_u64_add(sc->gnv_stats.tso, 1);
+ }
+ } else
+ ro = NULL;
+
+ error = ip_output(m, NULL, ro, 0, sc->gnv_im4o, NULL);
+ if (error == 0) {
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, plen);
+ if (mcast)
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+ } else
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ return (error);
+}
+#endif
+
+#ifdef INET6
+static int
+geneve_encap6(struct geneve_softc *sc, const union sockaddr_union *funsa,
+ struct mbuf *m)
+{
+ struct ifnet *ifp;
+ struct ip6_hdr *ip6;
+ struct ip6_pktopts opts;
+ struct sockaddr_in6 *sin6;
+ struct route_in6 route, *ro;
+ const struct in6_addr *srcaddr, *dstaddr;
+ int plen, error;
+ uint32_t csum_flags;
+ uint16_t srcport, dstport, proto;
+ u_short ip6_df;
+ uint8_t tos, ecn, etos, ttl;
+ bool mcast;
+
+ NET_EPOCH_ASSERT();
+
+ ifp = sc->gnv_ifp;
+ srcaddr = &sc->gnv_src_addr.sin6.sin6_addr;
+ srcport = htons(geneve_pick_source_port(sc, m));
+ dstaddr = &funsa->sin6.sin6_addr;
+ dstport = funsa->sin6.sin6_port;
+ plen = m->m_pkthdr.len;
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER)
+ proto = sc->gnv_proto;
+ else
+ proto = geneve_get_ethertype(m);
+
+ error = geneve_inherit_l3_hdr(m, sc, proto, &tos, &ttl, &ip6_df);
+ if (error) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (error);
+ }
+
+ ip6_initpktopts(&opts);
+ if (ip6_df)
+ opts.ip6po_flags = IP6PO_DONTFRAG;
+
+ M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct geneveudphdr), M_NOWAIT);
+ if (m == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENOBUFS);
+ }
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+
+ ecn = (tos & IPTOS_ECN_MASK);
+ ip_ecn_ingress(ECN_ALLOWED, &etos, &ecn);
+ ip6->ip6_flow |= htonl((u_int32_t)etos << IPV6_FLOWLABEL_LEN);
+ if (sc->gnv_flags & GENEVE_FLAG_DSCP_INHERIT)
+ ip6->ip6_flow |= htonl((u_int32_t)tos << IPV6_FLOWLABEL_LEN);
+
+ ip6->ip6_plen = 0;
+ ip6->ip6_nxt = IPPROTO_UDP;
+ ip6->ip6_hlim = ttl;
+ ip6->ip6_src = *srcaddr;
+ ip6->ip6_dst = *dstaddr;
+
+ geneve_encap_header(sc, m, sizeof(struct ip6_hdr), srcport, dstport,
+ htons(proto));
+ mcast = (m->m_flags & (M_MCAST | M_BCAST));
+ m->m_flags &= ~(M_MCAST | M_BCAST);
+
+ ro = NULL;
+ m->m_pkthdr.csum_flags &= CSUM_FLAGS_TX;
+ if (mcast || m->m_pkthdr.csum_flags != 0) {
+ /*
+ * HW checksum (L3 and/or L4) or TSO has been requested. Look
+ * up the ifnet for the outbound route and verify that the
+ * outbound ifnet can perform the requested operation on the
+ * inner frame.
+ * XXX: There's a rare scenario with ipv6 over multicast
+ * underlay where, when mc_ifname is set, it causes panics
+ * inside a jail. We'll force geneve to select its own outbound
+ * interface to avoid this.
+ */
+ memset(&route, 0, sizeof(route));
+ ro = &route;
+ sin6 = (struct sockaddr_in6 *)&ro->ro_dst;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_addr = ip6->ip6_dst;
+ ro->ro_nh = fib6_lookup(M_GETFIB(m), &ip6->ip6_dst, 0, NHR_NONE, 0);
+ if (ro->ro_nh == NULL) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (EHOSTUNREACH);
+ }
+ }
+ if (m->m_pkthdr.csum_flags != 0) {
+ csum_flags = csum_flags_to_inner_flags(m->m_pkthdr.csum_flags,
+ CSUM_ENCAP_GENEVE);
+ if ((csum_flags & ro->ro_nh->nh_ifp->if_hwassist) != csum_flags) {
+ if (ppsratecheck(&sc->err_time, &sc->err_pps, 1)) {
+ const struct ifnet *nh_ifp = ro->ro_nh->nh_ifp;
+
+ if_printf(ifp, "interface %s is missing hwcaps "
+ "0x%08x, csum_flags 0x%08x -> 0x%08x, "
+ "hwassist 0x%08x\n", nh_ifp->if_xname,
+ csum_flags & ~(uint32_t)nh_ifp->if_hwassist,
+ m->m_pkthdr.csum_flags, csum_flags,
+ (uint32_t)nh_ifp->if_hwassist);
+ }
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ENXIO);
+ }
+ m->m_pkthdr.csum_flags = csum_flags;
+ if (csum_flags &
+ (CSUM_INNER_IP | CSUM_INNER_IP_UDP | CSUM_INNER_IP6_UDP |
+ CSUM_INNER_IP_TCP | CSUM_INNER_IP6_TCP)) {
+ counter_u64_add(sc->gnv_stats.txcsum, 1);
+ if (csum_flags & CSUM_INNER_TSO)
+ counter_u64_add(sc->gnv_stats.tso, 1);
+ }
+ } else if (ntohs(dstport) != V_zero_checksum_port) {
+ struct udphdr *hdr = mtodo(m, sizeof(struct ip6_hdr));
+
+ hdr->uh_sum = in6_cksum_pseudo(ip6,
+ m->m_pkthdr.len - sizeof(struct ip6_hdr), IPPROTO_UDP, 0);
+ m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ }
+ error = ip6_output(m, &opts, ro, 0, sc->gnv_im6o, NULL, NULL);
+ if (error == 0) {
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, plen);
+ if (mcast)
+ if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
+ } else
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ return (error);
+}
+#endif
+
+static int
+geneve_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct rm_priotracker tracker;
+ union sockaddr_union unsa;
+ struct geneve_softc *sc;
+ struct gnv_ftable_entry *fe;
+ struct ifnet *mcifp;
+ struct ether_header *eh;
+ uint32_t af;
+ int error;
+
+ mcifp = NULL;
+ sc = ifp->if_softc;
+ GENEVE_RLOCK(sc, &tracker);
+ M_SETFIB(m, sc->gnv_fibnum);
+
+ if ((sc->gnv_flags & GENEVE_FLAG_RUNNING) == 0) {
+ GENEVE_RUNLOCK(sc, &tracker);
+ m_freem(m);
+ return (ENETDOWN);
+ }
+ if (__predict_false(if_tunnel_check_nesting(ifp, m,
+ MTAG_GENEVE_LOOP, 1) != 0)) {
+ GENEVE_RUNLOCK(sc, &tracker);
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (ELOOP);
+ }
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER) {
+ fe = NULL;
+ eh = mtod(m, struct ether_header *);
+
+ ETHER_BPF_MTAP(ifp, m);
+ if ((m->m_flags & (M_BCAST | M_MCAST)) == 0)
+ fe = geneve_ftable_entry_lookup(sc, eh->ether_dhost);
+ if (fe == NULL)
+ fe = &sc->gnv_default_fe;
+ geneve_sockaddr_copy(&unsa, &fe->gnvfe_raddr.sa);
+ } else
+ geneve_sockaddr_copy(&unsa, &sc->gnv_dst_addr.sa);
+
+ af = unsa.sa.sa_family;
+ if (geneve_check_multicast_addr(&unsa) != 0)
+ mcifp = geneve_multicast_if_ref(sc, af);
+
+ GENEVE_ACQUIRE(sc);
+ GENEVE_RUNLOCK(sc, &tracker);
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ error = geneve_encap4(sc, &unsa, m);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ error = geneve_encap6(sc, &unsa, m);
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ }
+
+ geneve_release(sc);
+ if (mcifp != NULL)
+ if_rele(mcifp);
+
+ return (error);
+}
+
+static int
+geneve_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
+ struct route *ro)
+{
+ uint32_t af;
+ int error;
+
+#ifdef MAC
+ error = mac_ifnet_check_transmit(ifp, m);
+ if (error) {
+ m_freem(m);
+ return (error);
+ }
+#endif
+
+ /* BPF writes need to be handled specially. */
+ if (dst->sa_family == AF_UNSPEC || dst->sa_family == pseudo_AF_HDRCMPLT)
+ memmove(&af, dst->sa_data, sizeof(af));
+ else
+ af = RO_GET_FAMILY(ro, dst);
+
+ BPF_MTAP2(ifp, &af, sizeof(af), m);
+ error = (ifp->if_transmit)(ifp, m);
+ if (error)
+ return (ENOBUFS);
+ return (0);
+}
+
+static int
+geneve_next_option(struct geneve_socket *gnvso, struct genevehdr *gnvh,
+ struct mbuf **m0)
+{
+ int optlen, error;
+
+ error = 0;
+ /*
+ * We MUST NOT forward the packet if control (O) bit is set
+ * and currently there is not standard specification for it.
+ * Therefore, we drop it.
+ */
+ if (gnvh->geneve_control)
+ return (EINVAL);
+
+ optlen = gnvh->geneve_optlen;
+ if (optlen == 0)
+ return (error);
+
+ /*
+ * XXX: Geneve options processing
+ * We MUST drop the packet if there are options to process
+ * and we are not able to process it.
+ */
+ if (gnvh->geneve_critical)
+ error = EINVAL;
+
+ return (error);
+}
+
+static void
+geneve_qflush(struct ifnet *ifp __unused)
+{
+}
+
+static void
+geneve_input_csum(struct mbuf *m, struct ifnet *ifp, counter_u64_t rxcsum)
+{
+ uint32_t csum_flags;
+
+ if ((((ifp->if_capenable & IFCAP_RXCSUM) != 0 &&
+ (m->m_pkthdr.csum_flags & CSUM_INNER_L3_CALC) != 0) ||
+ ((ifp->if_capenable & IFCAP_RXCSUM_IPV6) != 0 &&
+ (m->m_pkthdr.csum_flags & CSUM_INNER_L3_CALC) == 0))) {
+ csum_flags = 0;
+
+ if (m->m_pkthdr.csum_flags & CSUM_INNER_L3_CALC)
+ csum_flags |= CSUM_L3_CALC;
+ if (m->m_pkthdr.csum_flags & CSUM_INNER_L3_VALID)
+ csum_flags |= CSUM_L3_VALID;
+ if (m->m_pkthdr.csum_flags & CSUM_INNER_L4_CALC)
+ csum_flags |= CSUM_L4_CALC;
+ if (m->m_pkthdr.csum_flags & CSUM_INNER_L4_VALID)
+ csum_flags |= CSUM_L4_VALID;
+ m->m_pkthdr.csum_flags = csum_flags;
+ counter_u64_add(rxcsum, 1);
+ } else {
+ /* clear everything */
+ m->m_pkthdr.csum_flags = 0;
+ m->m_pkthdr.csum_data = 0;
+ }
+}
+
+static uint32_t
+geneve_map_etype_to_af(uint32_t ethertype)
+{
+
+ if (ethertype == ETHERTYPE_IP)
+ return (AF_INET);
+ if (ethertype == ETHERTYPE_IPV6)
+ return (AF_INET6);
+ if (ethertype == ETHERTYPE_ARP)
+ return (AF_LINK);
+ return (0);
+}
+
+static bool
+geneve_udp_input(struct mbuf *m, int offset, struct inpcb *inpcb,
+ const struct sockaddr *srcsa, void *xgnvso)
+{
+ struct geneve_socket *gnvso;
+ struct geneve_pkt_info info;
+ struct genevehdr *gnvh, gnvhdr;
+ struct geneve_softc *sc;
+ struct ip *iphdr;
+ struct ip6_hdr *ip6hdr;
+ struct ifnet *ifp;
+ int32_t plen, af;
+ uint32_t vni;
+ uint16_t optlen, proto;
+ int error;
+
+ M_ASSERTPKTHDR(m);
+ plen = m->m_pkthdr.len;
+ gnvso = xgnvso;
+
+ if (m->m_pkthdr.len < offset + sizeof(struct geneveudphdr))
+ return (false);
+
+ /* Get ECN and TTL values for future processing */
+ memset(&info, 0, sizeof(info));
+ info.ethertype = geneve_get_ethertype(m);
+ if (info.ethertype == ETHERTYPE_IP) {
+ iphdr = mtodo(m, offset - sizeof(struct ip));
+ info.ecn = (iphdr->ip_tos & IPTOS_ECN_MASK);
+ info.ttl = iphdr->ip_ttl;
+ } else if (info.ethertype == ETHERTYPE_IPV6) {
+ ip6hdr = mtodo(m, offset - sizeof(struct ip6_hdr));
+ info.ecn = IPV6_ECN(ip6hdr);
+ info.ttl = ip6hdr->ip6_hlim;
+ }
+
+ /* Get geneve header */
+ offset += sizeof(struct udphdr);
+ if (__predict_false(m->m_len < offset + sizeof(struct genevehdr))) {
+ m_copydata(m, offset, sizeof(struct genevehdr), (caddr_t)&gnvhdr);
+ gnvh = &gnvhdr;
+ } else
+ gnvh = mtodo(m, offset);
+
+ /*
+ * Drop if there is a reserved bit or unknown version set in the header.
+ * As defined in RFC 8926 3.4
+ */
+ if (gnvh->geneve_ver != htons(GENEVE_VERSION) ||
+ gnvh->geneve_vni & ~GENEVE_VNI_MASK)
+ return (false);
+
+ /*
+ * The length of the option fields, expressed in 4-byte multiples, not
+ * including the 8-byte fixed tunnel header.
+ */
+ optlen = ntohs(gnvh->geneve_optlen) * 4;
+ error = geneve_next_option(gnvso, gnvh, &m);
+ if (error != 0)
+ return (false);
+
+ vni = ntohl(gnvh->geneve_vni) >> GENEVE_HDR_VNI_SHIFT;
+ sc = geneve_socket_lookup_softc(gnvso, vni);
+ if (sc == NULL)
+ return (false);
+
+ if ((sc->gnv_flags & GENEVE_FLAG_RUNNING) == 0)
+ goto out;
+
+ proto = ntohs(gnvh->geneve_proto);
+ m_adj(m, offset + sizeof(struct genevehdr) + optlen);
+
+ /* if next protocol is ethernet, check its ethertype and learn it */
+ if (proto == GENEVE_PROTO_ETHER) {
+ offset = ETHER_HDR_LEN;
+ error = geneve_input_ether(sc, &m, srcsa, &info);
+ if (error != 0)
+ goto out;
+ } else {
+ info.ethertype = proto;
+ af = geneve_map_etype_to_af(info.ethertype);
+ offset = 0;
+ }
+
+ error = geneve_input_inherit(sc, &m, offset, &info);
+ if (error != 0)
+ goto out;
+
+ ifp = sc->gnv_ifp;
+ if (ifp == m->m_pkthdr.rcvif)
+ /* XXX Does not catch more complex loops. */
+ goto out;
+
+ m_clrprotoflags(m);
+ m->m_pkthdr.rcvif = ifp;
+ M_SETFIB(m, ifp->if_fib);
+ geneve_input_csum(m, ifp, sc->gnv_stats.rxcsum);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_IBYTES, plen);
+ if (sc->gnv_mc_ifp != NULL)
+ if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1);
+
+ MPASS(m != NULL);
+
+ if (proto == GENEVE_PROTO_ETHER)
+ (*ifp->if_input)(ifp, m);
+ else {
+ BPF_MTAP2(ifp, &af, sizeof(af), m);
+ netisr_dispatch_src(info.isr, (uintptr_t)xgnvso, m);
+ }
+
+ m = NULL;
+out:
+ geneve_release(sc);
+ if (m != NULL) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ }
+
+ return (true);
+}
+
+static int
+geneve_input_ether(struct geneve_softc *sc, struct mbuf **m0,
+ const struct sockaddr *sa, struct geneve_pkt_info *info)
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+
+ m = *m0;
+
+ if (sc->gnv_proto != GENEVE_PROTO_ETHER)
+ return (EPROTOTYPE);
+
+ if (m->m_pkthdr.len < ETHER_HDR_LEN)
+ return (EINVAL);
+
+ if (m->m_len < ETHER_HDR_LEN &&
+ (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+
+ eh = mtod(m, struct ether_header *);
+ info->ethertype = ntohs(eh->ether_type);
+ if (sc->gnv_flags & GENEVE_FLAG_LEARN)
+ geneve_ftable_learn(sc, sa, eh->ether_shost);
+
+ *m0 = m;
+ return (0);
+}
+
+static int
+geneve_input_inherit(struct geneve_softc *sc, struct mbuf **m0,
+ int offset, struct geneve_pkt_info *info)
+{
+ struct mbuf *m;
+ struct ip *iphdr;
+ struct ip6_hdr *ip6hdr;
+ uint8_t itos;
+
+ m = *m0;
+
+ switch (info->ethertype) {
+ case ETHERTYPE_IP:
+ offset += sizeof(struct ip);
+ if (m->m_pkthdr.len < offset)
+ return (EINVAL);
+
+ if (m->m_len < offset &&
+ (m = m_pullup(m, offset)) == NULL) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+ iphdr = mtodo(m, offset - sizeof(struct ip));
+
+ if (ip_ecn_egress(ECN_COMPLETE, &info->ecn, &iphdr->ip_tos) == 0) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+
+ if ((sc->gnv_flags & GENEVE_FLAG_TTL_INHERIT) != 0 && info->ttl > 0)
+ iphdr->ip_ttl = info->ttl;
+
+ info->isr = NETISR_IP;
+ break;
+
+ case ETHERTYPE_IPV6:
+ offset += sizeof(struct ip6_hdr);
+ if (m->m_pkthdr.len < offset)
+ return (EINVAL);
+
+ if (m->m_len < offset &&
+ (m = m_pullup(m, offset)) == NULL) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+ ip6hdr = mtodo(m, offset - sizeof(struct ip6_hdr));
+
+ itos = (ntohl(ip6hdr->ip6_flow) >> IPV6_FLOWLABEL_LEN) & 0xff;
+ if (ip_ecn_egress(ECN_COMPLETE, &info->ecn, &itos) == 0) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+ ip6hdr->ip6_flow |= htonl((uint32_t)itos << IPV6_FLOWLABEL_LEN);
+
+ if ((sc->gnv_flags & GENEVE_FLAG_TTL_INHERIT) && (info->ttl > 0))
+ ip6hdr->ip6_hlim = info->ttl;
+
+ info->isr = NETISR_IPV6;
+ break;
+
+ case ETHERTYPE_ARP:
+ if (sc->gnv_proto == GENEVE_PROTO_INHERIT)
+ return (EINVAL);
+
+ offset += sizeof(struct arphdr);
+ if (m->m_pkthdr.len < offset)
+ return (EINVAL);
+
+ if (m->m_len < offset &&
+ (m = m_pullup(m, offset)) == NULL) {
+ *m0 = NULL;
+ return (ENOBUFS);
+ }
+ info->isr = NETISR_ARP;
+ break;
+
+ default:
+ if_inc_counter(sc->gnv_ifp, IFCOUNTER_NOPROTO, 1);
+ return (EINVAL);
+ }
+
+ *m0 = m;
+ return (0);
+}
+
+static void
+geneve_stats_alloc(struct geneve_softc *sc)
+{
+ struct geneve_statistics *stats = &sc->gnv_stats;
+
+ stats->txcsum = counter_u64_alloc(M_WAITOK);
+ stats->tso = counter_u64_alloc(M_WAITOK);
+ stats->rxcsum = counter_u64_alloc(M_WAITOK);
+}
+
+static void
+geneve_stats_free(struct geneve_softc *sc)
+{
+ struct geneve_statistics *stats = &sc->gnv_stats;
+
+ counter_u64_free(stats->txcsum);
+ counter_u64_free(stats->tso);
+ counter_u64_free(stats->rxcsum);
+}
+
+static void
+geneve_set_default_config(struct geneve_softc *sc)
+{
+
+ sc->gnv_flags |= GENEVE_FLAG_LEARN;
+
+ sc->gnv_vni = GENEVE_VNI_MAX;
+ sc->gnv_ttl = V_ip_defttl;
+
+ sc->gnv_src_addr.sin.sin_port = htons(GENEVE_UDPPORT);
+ sc->gnv_dst_addr.sin.sin_port = htons(GENEVE_UDPPORT);
+
+ /*
+ * RFC 8926 Section 3.3, the entire 16-bit range MAY
+ * be used to maximize entropy.
+ */
+ sc->gnv_min_port = V_ipport_firstauto;
+ sc->gnv_max_port = V_ipport_lastauto;
+
+ sc->gnv_proto = GENEVE_PROTO_ETHER;
+
+ sc->gnv_ftable_max = GENEVE_FTABLE_MAX;
+ sc->gnv_ftable_timeout = GENEVE_FTABLE_TIMEOUT;
+}
+
+static int
+geneve_set_reqcap(struct geneve_softc *sc, struct ifnet *ifp, int reqcap,
+ int reqcap2)
+{
+ int mask = reqcap ^ ifp->if_capenable;
+
+ /* Disable TSO if tx checksums are disabled. */
+ if (mask & IFCAP_TXCSUM && !(reqcap & IFCAP_TXCSUM) &&
+ reqcap & IFCAP_TSO4) {
+ reqcap &= ~IFCAP_TSO4;
+ if_printf(ifp, "tso4 disabled due to -txcsum.\n");
+ }
+ if (mask & IFCAP_TXCSUM_IPV6 && !(reqcap & IFCAP_TXCSUM_IPV6) &&
+ reqcap & IFCAP_TSO6) {
+ reqcap &= ~IFCAP_TSO6;
+ if_printf(ifp, "tso6 disabled due to -txcsum6.\n");
+ }
+
+ /* Do not enable TSO if tx checksums are disabled. */
+ if (mask & IFCAP_TSO4 && reqcap & IFCAP_TSO4 &&
+ !(reqcap & IFCAP_TXCSUM)) {
+ if_printf(ifp, "enable txcsum first.\n");
+ return (EAGAIN);
+ }
+ if (mask & IFCAP_TSO6 && reqcap & IFCAP_TSO6 &&
+ !(reqcap & IFCAP_TXCSUM_IPV6)) {
+ if_printf(ifp, "enable txcsum6 first.\n");
+ return (EAGAIN);
+ }
+
+ sc->gnv_reqcap = reqcap;
+ sc->gnv_reqcap2 = reqcap2;
+ return (0);
+}
+
+/*
+ * A GENEVE interface inherits the capabilities of the genevedev or the interface
+ * hosting the genevelocal address.
+ */
+static void
+geneve_set_hwcaps(struct geneve_softc *sc)
+{
+ struct epoch_tracker et;
+ struct ifnet *p, *ifp;
+ struct ifaddr *ifa;
+ u_long hwa;
+ int cap, ena;
+ bool rel;
+
+ /* reset caps */
+ ifp = sc->gnv_ifp;
+ ifp->if_capabilities &= GENEVE_BASIC_IFCAPS;
+ ifp->if_capenable &= GENEVE_BASIC_IFCAPS;
+ ifp->if_hwassist = 0;
+
+ NET_EPOCH_ENTER(et);
+ CURVNET_SET(ifp->if_vnet);
+
+ p = NULL;
+ rel = false;
+ if (sc->gnv_mc_ifname[0] != '\0') {
+ rel = true;
+ p = ifunit_ref(sc->gnv_mc_ifname);
+ } else if (geneve_sockaddr_in_any(&sc->gnv_src_addr) == 0) {
+ if (sc->gnv_src_addr.sa.sa_family == AF_INET) {
+ struct sockaddr_in in4 = sc->gnv_src_addr.sin;
+
+ in4.sin_port = 0;
+ ifa = ifa_ifwithaddr((struct sockaddr *)&in4);
+ if (ifa != NULL)
+ p = ifa->ifa_ifp;
+ } else if (sc->gnv_src_addr.sa.sa_family == AF_INET6) {
+ struct sockaddr_in6 in6 = sc->gnv_src_addr.sin6;
+
+ in6.sin6_port = 0;
+ ifa = ifa_ifwithaddr((struct sockaddr *)&in6);
+ if (ifa != NULL)
+ p = ifa->ifa_ifp;
+ }
+ }
+ if (p == NULL) {
+ CURVNET_RESTORE();
+ NET_EPOCH_EXIT(et);
+ return;
+ }
+
+ cap = ena = hwa = 0;
+
+ /* checksum offload */
+ if ((p->if_capabilities2 & IFCAP2_BIT(IFCAP2_GENEVE_HWCSUM)) != 0)
+ cap |= p->if_capabilities & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6);
+ if ((p->if_capenable2 & IFCAP2_BIT(IFCAP2_GENEVE_HWCSUM)) != 0) {
+ ena |= sc->gnv_reqcap & p->if_capenable & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6);
+ if (ena & IFCAP_TXCSUM) {
+ if (p->if_hwassist & CSUM_INNER_IP)
+ hwa |= CSUM_IP;
+ if (p->if_hwassist & CSUM_INNER_IP_UDP)
+ hwa |= CSUM_IP_UDP;
+ if (p->if_hwassist & CSUM_INNER_IP_TCP)
+ hwa |= CSUM_IP_TCP;
+ }
+ if (ena & IFCAP_TXCSUM_IPV6) {
+ if (p->if_hwassist & CSUM_INNER_IP6_UDP)
+ hwa |= CSUM_IP6_UDP;
+ if (p->if_hwassist & CSUM_INNER_IP6_TCP)
+ hwa |= CSUM_IP6_TCP;
+ }
+ }
+
+ /* hardware TSO */
+ if ((p->if_capabilities2 & IFCAP2_BIT(IFCAP2_GENEVE_HWTSO)) != 0) {
+ cap |= p->if_capabilities & IFCAP_TSO;
+ if (p->if_hw_tsomax > IP_MAXPACKET - ifp->if_hdrlen)
+ ifp->if_hw_tsomax = IP_MAXPACKET - ifp->if_hdrlen;
+ else
+ ifp->if_hw_tsomax = p->if_hw_tsomax;
+ ifp->if_hw_tsomaxsegcount = p->if_hw_tsomaxsegcount - 1;
+ ifp->if_hw_tsomaxsegsize = p->if_hw_tsomaxsegsize;
+ }
+ if ((p->if_capenable2 & IFCAP2_BIT(IFCAP2_GENEVE_HWTSO)) != 0) {
+ ena |= sc->gnv_reqcap & p->if_capenable & IFCAP_TSO;
+ if (ena & IFCAP_TSO) {
+ if (p->if_hwassist & CSUM_INNER_IP_TSO)
+ hwa |= CSUM_IP_TSO;
+ if (p->if_hwassist & CSUM_INNER_IP6_TSO)
+ hwa |= CSUM_IP6_TSO;
+ }
+ }
+
+ ifp->if_capabilities |= cap;
+ ifp->if_capenable |= ena;
+ ifp->if_hwassist |= hwa;
+ if (rel)
+ if_rele(p);
+
+ CURVNET_RESTORE();
+ NET_EPOCH_EXIT(et);
+}
+
+static int
+geneve_clone_create_nl(struct if_clone *ifc, char *name, size_t len,
+ struct ifc_data_nl *ifd)
+{
+ struct nl_parsed_link *lattrs = ifd->lattrs;
+ struct nl_pstate *npt = ifd->npt;
+ struct nl_parsed_geneve attrs = {};
+ int error;
+
+ if ((lattrs->ifla_idata == NULL) ||
+ (!nl_has_attr(ifd->bm, IFLA_LINKINFO))) {
+ nlmsg_report_err_msg(npt, "geneve protocol is required");
+ return (ENOTSUP);
+ }
+
+ error = nl_parse_nested(lattrs->ifla_idata, &geneve_create_parser, npt, &attrs);
+ if (error != 0)
+ return (error);
+ if (geneve_check_proto(attrs.ifla_proto)) {
+ nlmsg_report_err_msg(npt, "Unsupported ethertype: 0x%04X", attrs.ifla_proto);
+ return (ENOTSUP);
+ }
+
+ struct geneve_params gnvp = { .ifla_proto = attrs.ifla_proto };
+ struct ifc_data ifd_new = {
+ .flags = IFC_F_SYSSPACE,
+ .unit = ifd->unit,
+ .params = &gnvp
+ };
+
+ return (geneve_clone_create(ifc, name, len, &ifd_new, &ifd->ifp));
+}
+
+static int
+geneve_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
+{
+ struct geneve_softc *sc = ifp->if_softc;
+ struct nl_parsed_link *lattrs = ifd->lattrs;
+ struct nl_pstate *npt = ifd->npt;
+ struct nl_parsed_geneve params;
+ struct nlattr *attrs = lattrs->ifla_idata;
+ struct nlattr_bmask bm;
+ int error = 0;
+
+ if ((attrs == NULL) ||
+ (nl_has_attr(ifd->bm, IFLA_LINKINFO) == 0)) {
+ error = nl_modify_ifp_generic(ifp, lattrs, ifd->bm, npt);
+ return (error);
+ }
+
+ error = priv_check(curthread, PRIV_NET_GENEVE);
+ if (error)
+ return (error);
+
+ /* make sure ignored attributes by nl_parse will not cause panics */
+ memset(&params, 0, sizeof(params));
+
+ nl_get_attrs_bmask_raw(NLA_DATA(attrs), NLA_DATA_LEN(attrs), &bm);
+ error = nl_parse_nested(attrs, &geneve_modify_parser, npt, &params);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_ID))
+ error = geneve_set_vni_nl(sc, npt, params.ifla_vni);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_LOCAL))
+ error = geneve_set_local_addr_nl(sc, npt, params.ifla_local);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_REMOTE))
+ error = geneve_set_remote_addr_nl(sc, npt, params.ifla_remote);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_LOCAL_PORT))
+ error = geneve_set_local_port_nl(sc, npt, params.ifla_local_port);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_PORT))
+ error = geneve_set_remote_port_nl(sc, npt, params.ifla_remote_port);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_PORT_RANGE))
+ error = geneve_set_port_range_nl(sc, npt, params.ifla_port_range);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_DF))
+ error = geneve_set_df_nl(sc, npt, params.ifla_df);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_TTL))
+ error = geneve_set_ttl_nl(sc, npt, params.ifla_ttl);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_TTL_INHERIT))
+ error = geneve_set_ttl_inherit_nl(sc, npt, params.ifla_ttl_inherit);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_DSCP_INHERIT))
+ error = geneve_set_dscp_inherit_nl(sc, npt, params.ifla_dscp_inherit);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_COLLECT_METADATA))
+ error = geneve_set_collect_metadata_nl(sc, npt, params.ifla_external);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_FTABLE_LEARN))
+ error = geneve_set_learn_nl(sc, npt, params.ifla_ftable_learn);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_FTABLE_FLUSH))
+ error = geneve_flush_ftable_nl(sc, npt, params.ifla_ftable_flush);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_FTABLE_MAX))
+ error = geneve_set_ftable_max_nl(sc, npt, params.ifla_ftable_max);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_FTABLE_TIMEOUT))
+ error = geneve_set_ftable_timeout_nl(sc, npt, params.ifla_ftable_timeout);
+
+ if (error == 0 && nl_has_attr(&bm, IFLA_GENEVE_MC_IFNAME))
+ error = geneve_set_mc_if_nl(sc, npt, params.ifla_mc_ifname);
+
+ if (error == 0)
+ error = nl_modify_ifp_generic(ifp, lattrs, ifd->bm, npt);
+
+ return (error);
+}
+
+static void
+geneve_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
+{
+ struct geneve_softc *sc;
+ struct rm_priotracker tracker;
+ int off, off2;
+
+ nlattr_add_u32(nw, IFLA_LINK, ifp->if_index);
+ nlattr_add_string(nw, IFLA_IFNAME, ifp->if_xname);
+
+ off = nlattr_add_nested(nw, IFLA_LINKINFO);
+ if (off == 0)
+ return;
+
+ nlattr_add_string(nw, IFLA_INFO_KIND, "geneve");
+ off2 = nlattr_add_nested(nw, IFLA_INFO_DATA);
+ if (off2 == 0) {
+ nlattr_set_len(nw, off);
+ return;
+ }
+
+ sc = ifp->if_softc;
+ GENEVE_RLOCK(sc, &tracker);
+
+ nlattr_add_u32(nw, IFLA_GENEVE_ID, sc->gnv_vni);
+ nlattr_add_u16(nw, IFLA_GENEVE_PROTOCOL, sc->gnv_proto);
+ geneve_get_local_addr_nl(sc, nw);
+ geneve_get_remote_addr_nl(sc, nw);
+ nlattr_add_u16(nw, IFLA_GENEVE_LOCAL_PORT, geneve_get_local_port(sc));
+ nlattr_add_u16(nw, IFLA_GENEVE_PORT, geneve_get_remote_port(sc));
+
+ const struct ifla_geneve_port_range port_range = {
+ .low = sc->gnv_min_port,
+ .high = sc->gnv_max_port
+ };
+ nlattr_add(nw, IFLA_GENEVE_PORT_RANGE, sizeof(port_range), &port_range);
+
+ nlattr_add_u8(nw, IFLA_GENEVE_DF, (uint8_t)sc->gnv_df);
+ nlattr_add_u8(nw, IFLA_GENEVE_TTL, sc->gnv_ttl);
+ nlattr_add_bool(nw, IFLA_GENEVE_TTL_INHERIT,
+ sc->gnv_flags & GENEVE_FLAG_TTL_INHERIT);
+ nlattr_add_bool(nw, IFLA_GENEVE_DSCP_INHERIT,
+ sc->gnv_flags & GENEVE_FLAG_DSCP_INHERIT);
+ nlattr_add_bool(nw, IFLA_GENEVE_COLLECT_METADATA,
+ sc->gnv_flags & GENEVE_FLAG_COLLECT_METADATA);
+
+ nlattr_add_bool(nw, IFLA_GENEVE_FTABLE_LEARN,
+ sc->gnv_flags & GENEVE_FLAG_LEARN);
+ nlattr_add_u32(nw, IFLA_GENEVE_FTABLE_MAX, sc->gnv_ftable_max);
+ nlattr_add_u32(nw, IFLA_GENEVE_FTABLE_TIMEOUT, sc->gnv_ftable_timeout);
+ nlattr_add_u32(nw, IFLA_GENEVE_FTABLE_COUNT, sc->gnv_ftable_cnt);
+ nlattr_add_u32(nw, IFLA_GENEVE_FTABLE_NOSPACE_CNT, sc->gnv_stats.ftable_nospace);
+ nlattr_add_u32(nw, IFLA_GENEVE_FTABLE_LOCK_UP_FAIL_CNT,
+ sc->gnv_stats.ftable_lock_upgrade_failed);
+
+ nlattr_add_string(nw, IFLA_GENEVE_MC_IFNAME, sc->gnv_mc_ifname);
+ nlattr_add_u32(nw, IFLA_GENEVE_MC_IFINDEX, sc->gnv_mc_ifindex);
+
+ nlattr_add_u64(nw, IFLA_GENEVE_TXCSUM_CNT,
+ counter_u64_fetch(sc->gnv_stats.txcsum));
+ nlattr_add_u64(nw, IFLA_GENEVE_TSO_CNT,
+ counter_u64_fetch(sc->gnv_stats.tso));
+ nlattr_add_u64(nw, IFLA_GENEVE_RXCSUM_CNT,
+ counter_u64_fetch(sc->gnv_stats.rxcsum));
+
+ nlattr_set_len(nw, off2);
+ nlattr_set_len(nw, off);
+
+ GENEVE_RUNLOCK(sc, &tracker);
+}
+
+static int
+geneve_clone_create(struct if_clone *ifc, char *name, size_t len,
+ struct ifc_data *ifd, struct ifnet **ifpp)
+{
+ struct geneve_softc *sc;
+ struct geneve_params gnvp;
+ struct ifnet *ifp;
+ int error;
+
+ sc = malloc(sizeof(struct geneve_softc), M_GENEVE, M_WAITOK | M_ZERO);
+ sc->gnv_fibnum = curthread->td_proc->p_fibnum;
+ geneve_set_default_config(sc);
+
+ if (ifd != NULL) {
+ error = ifc_copyin(ifd, &gnvp, sizeof(gnvp));
+ if (error != 0 ||
+ (error = geneve_check_proto(gnvp.ifla_proto)) != 0) {
+ free(sc, M_GENEVE);
+ return (error);
+ }
+
+ sc->gnv_proto = gnvp.ifla_proto;
+ }
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER) {
+ ifp = if_alloc(IFT_ETHER);
+ ifp->if_flags |= IFF_SIMPLEX | IFF_BROADCAST;
+ geneve_ftable_init(sc);
+ callout_init_rw(&sc->gnv_callout, &sc->gnv_lock, 0);
+ } else if (sc->gnv_proto == GENEVE_PROTO_INHERIT) {
+ ifp = if_alloc(IFT_TUNNEL);
+ ifp->if_flags |= IFF_NOARP;
+ } else {
+ free(sc, M_GENEVE);
+ return (EINVAL);
+ }
+
+ geneve_stats_alloc(sc);
+ sc->gnv_ifp = ifp;
+ rm_init(&sc->gnv_lock, "geneverm");
+ sc->gnv_port_hash_key = arc4random();
+
+ ifp->if_softc = sc;
+ if_initname(ifp, geneve_name, ifd->unit);
+ ifp->if_flags |= IFF_MULTICAST;
+ ifp->if_init = geneve_init;
+ ifp->if_ioctl = geneve_ioctl;
+ ifp->if_transmit = geneve_transmit;
+ ifp->if_qflush = geneve_qflush;
+ ifp->if_capabilities = GENEVE_BASIC_IFCAPS;
+ ifp->if_capenable = GENEVE_BASIC_IFCAPS;
+ sc->gnv_reqcap = -1;
+ geneve_set_hwcaps(sc);
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER) {
+ ifmedia_init(&sc->gnv_media, 0, geneve_media_change, geneve_media_status);
+ ifmedia_add(&sc->gnv_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->gnv_media, IFM_ETHER | IFM_AUTO);
+
+ ether_gen_addr(ifp, &sc->gnv_hwaddr);
+ ether_ifattach(ifp, sc->gnv_hwaddr.octet);
+
+ ifp->if_baudrate = 0;
+ } else {
+ ifp->if_output = geneve_output;
+
+ if_attach(ifp);
+ bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
+ }
+
+ GENEVE_WLOCK(sc);
+ geneve_setup_interface_hdrlen(sc);
+ GENEVE_WUNLOCK(sc);
+ *ifpp = ifp;
+
+ return (0);
+}
+
+static int
+geneve_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
+{
+ struct geneve_softc *sc;
+
+ sc = if_getsoftc(ifp);
+ geneve_teardown(sc);
+
+ if (sc->gnv_proto == GENEVE_PROTO_ETHER) {
+ geneve_ftable_flush(sc, 1);
+
+ ether_ifdetach(ifp);
+ if_free(ifp);
+ ifmedia_removeall(&sc->gnv_media);
+
+ geneve_ftable_fini(sc);
+ } else {
+ bpfdetach(ifp);
+ if_detach(ifp);
+ if_free(ifp);
+ }
+
+ rm_destroy(&sc->gnv_lock);
+ geneve_stats_free(sc);
+ free(sc, M_GENEVE);
+
+ return (0);
+}
+
+/* BMV: Taken from if_bridge. */
+static uint32_t
+geneve_mac_hash(struct geneve_softc *sc, const uint8_t *addr)
+{
+ uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->gnv_ftable_hash_key;
+
+ b += addr[5] << 8;
+ b += addr[4];
+ a += addr[3] << 24;
+ a += addr[2] << 16;
+ a += addr[1] << 8;
+ a += addr[0];
+
+/*
+ * The following hash function is adapted from "Hash Functions" by Bob Jenkins
+ * ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
+ */
+#define mix(a, b, c) \
+do { \
+ a -= b; a -= c; a ^= (c >> 13); \
+ b -= c; b -= a; b ^= (a << 8); \
+ c -= a; c -= b; c ^= (b >> 13); \
+ a -= b; a -= c; a ^= (c >> 12); \
+ b -= c; b -= a; b ^= (a << 16); \
+ c -= a; c -= b; c ^= (b >> 5); \
+ a -= b; a -= c; a ^= (c >> 3); \
+ b -= c; b -= a; b ^= (a << 10); \
+ c -= a; c -= b; c ^= (b >> 15); \
+} while (0)
+
+ mix(a, b, c);
+
+#undef mix
+
+ return (c);
+}
+
+static int
+geneve_media_change(struct ifnet *ifp)
+{
+
+ /* Ignore. */
+ return (0);
+}
+
+static void
+geneve_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+
+ ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER | IFM_FDX;
+}
+
+static int
+geneve_sockaddr_cmp(const union sockaddr_union *unsa,
+ const struct sockaddr *sa)
+{
+
+ return (memcmp(&unsa->sa, sa, unsa->sa.sa_len));
+}
+
+static void
+geneve_sockaddr_copy(union sockaddr_union *dst,
+ const struct sockaddr *sa)
+{
+
+ MPASS(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+ memset(dst, 0, sizeof(*dst));
+
+ if (sa->sa_family == AF_INET) {
+ dst->sin = *SATOCONSTSIN(sa);
+ dst->sin.sin_len = sizeof(struct sockaddr_in);
+ } else if (sa->sa_family == AF_INET6) {
+ dst->sin6 = *SATOCONSTSIN6(sa);
+ dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
+ }
+}
+
+static int
+geneve_sockaddr_in_equal(const union sockaddr_union *unsa,
+ const struct sockaddr *sa)
+{
+ int equal;
+
+ if (sa->sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ equal = in4->s_addr == unsa->sin.sin_addr.s_addr;
+ } else if (sa->sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ equal = IN6_ARE_ADDR_EQUAL(in6, &unsa->sin6.sin6_addr);
+ } else
+ equal = 0;
+
+ return (equal);
+}
+
+static void
+geneve_sockaddr_in_copy(union sockaddr_union *dst,
+ const struct sockaddr *sa)
+{
+
+ MPASS(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ if (sa->sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ dst->sin.sin_family = AF_INET;
+ dst->sin.sin_len = sizeof(struct sockaddr_in);
+ dst->sin.sin_addr = *in4;
+ } else if (sa->sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ dst->sin6.sin6_family = AF_INET6;
+ dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6.sin6_addr = *in6;
+ }
+}
+
+static int
+geneve_sockaddr_supported(const union sockaddr_union *gnvaddr, int unspec)
+{
+ const struct sockaddr *sa;
+ int supported;
+
+ sa = &gnvaddr->sa;
+ supported = 0;
+
+ if (sa->sa_family == AF_UNSPEC && unspec != 0) {
+ supported = 1;
+ } else if (sa->sa_family == AF_INET) {
+ supported = 1;
+ } else if (sa->sa_family == AF_INET6) {
+ supported = 1;
+ }
+
+ return (supported);
+}
+
+static int
+geneve_sockaddr_in_any(const union sockaddr_union *gnvaddr)
+{
+ const struct sockaddr *sa;
+ int any;
+
+ sa = &gnvaddr->sa;
+
+ if (sa->sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ any = in4->s_addr == INADDR_ANY;
+ } else if (sa->sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ any = IN6_IS_ADDR_UNSPECIFIED(in6);
+ } else
+ any = -1;
+
+ return (any);
+}
+
+static int
+geneve_can_change_config(struct geneve_softc *sc)
+{
+
+ GENEVE_LOCK_ASSERT(sc);
+
+ if (sc->gnv_flags & GENEVE_FLAG_RUNNING)
+ return (0);
+ if (sc->gnv_flags & (GENEVE_FLAG_INIT | GENEVE_FLAG_TEARDOWN))
+ return (0);
+ if (sc->gnv_flags & GENEVE_FLAG_COLLECT_METADATA)
+ return (0);
+
+ return (1);
+}
+
+static int
+geneve_check_proto(uint16_t proto)
+{
+ int error;
+
+ switch (proto) {
+ case GENEVE_PROTO_ETHER:
+ case GENEVE_PROTO_INHERIT:
+ error = 0;
+ break;
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+geneve_check_multicast_addr(const union sockaddr_union *sa)
+{
+ int mc;
+
+ if (sa->sa.sa_family == AF_INET) {
+ const struct in_addr *in4 = &SATOCONSTSIN(sa)->sin_addr;
+ mc = IN_MULTICAST(ntohl(in4->s_addr));
+ } else if (sa->sa.sa_family == AF_INET6) {
+ const struct in6_addr *in6 = &SATOCONSTSIN6(sa)->sin6_addr;
+ mc = IN6_IS_ADDR_MULTICAST(in6);
+ } else
+ mc = EINVAL;
+
+ return (mc);
+}
+
+static int
+geneve_check_sockaddr(const union sockaddr_union *sa, const int len)
+{
+ int error;
+
+ error = 0;
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len < sizeof(struct sockaddr))
+ error = EINVAL;
+ break;
+
+ default:
+ error = EAFNOSUPPORT;
+ }
+
+ return (error);
+}
+
+static int
+geneve_prison_remove(void *obj, void *data __unused)
+{
+#ifdef VIMAGE
+ struct prison *pr;
+
+ pr = obj;
+ if (prison_owns_vnet(pr)) {
+ CURVNET_SET(pr->pr_vnet);
+ if (V_geneve_cloner != NULL) {
+ ifc_detach_cloner(V_geneve_cloner);
+ V_geneve_cloner = NULL;
+ }
+ CURVNET_RESTORE();
+ }
+#endif
+ return (0);
+}
+
+static void
+vnet_geneve_load(void)
+{
+ struct if_clone_addreq_v2 req = {
+ .version = 2,
+ .flags = IFC_F_AUTOUNIT,
+ .match_f = NULL,
+ .create_f = geneve_clone_create,
+ .destroy_f = geneve_clone_destroy,
+ .create_nl_f = geneve_clone_create_nl,
+ .modify_nl_f = geneve_clone_modify_nl,
+ .dump_nl_f = geneve_clone_dump_nl,
+ };
+ V_geneve_cloner = ifc_attach_cloner(geneve_name, (struct if_clone_addreq *)&req);
+}
+VNET_SYSINIT(vnet_geneve_load, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_geneve_load, NULL);
+
+static void
+vnet_geneve_unload(void)
+{
+
+ if (V_geneve_cloner != NULL)
+ ifc_detach_cloner(V_geneve_cloner);
+}
+VNET_SYSUNINIT(vnet_geneve_unload, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_geneve_unload, NULL);
+
+static void
+geneve_module_init(void)
+{
+ mtx_init(&geneve_list_mtx, "geneve list", NULL, MTX_DEF);
+ osd_method_t methods[PR_MAXMETHOD] = {
+ [PR_METHOD_REMOVE] = geneve_prison_remove,
+ };
+
+ geneve_osd_jail_slot = osd_jail_register(NULL, methods);
+ NL_VERIFY_PARSERS(all_parsers);
+}
+
+static void
+geneve_module_deinit(void)
+{
+ struct if_clone *clone;
+ VNET_ITERATOR_DECL(vnet_iter);
+
+ VNET_LIST_RLOCK();
+ VNET_FOREACH(vnet_iter) {
+ clone = VNET_VNET(vnet_iter, geneve_cloner);
+ if (clone != NULL) {
+ ifc_detach_cloner(clone);
+ VNET_VNET(vnet_iter, geneve_cloner) = NULL;
+ }
+ }
+ VNET_LIST_RUNLOCK();
+ NET_EPOCH_WAIT();
+ MPASS(LIST_EMPTY(&geneve_socket_list));
+ mtx_destroy(&geneve_list_mtx);
+ if (geneve_osd_jail_slot != 0)
+ osd_jail_deregister(geneve_osd_jail_slot);
+}
+
+static int
+geneve_modevent(module_t mod, int type, void *unused)
+{
+ int error;
+
+ error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ geneve_module_init();
+ break;
+
+ case MOD_UNLOAD:
+ geneve_module_deinit();
+ break;
+
+ default:
+ error = ENOTSUP;
+ break;
+ }
+
+ return (error);
+}
+
+static moduledata_t geneve_mod = {
+ "if_geneve",
+ geneve_modevent,
+ 0
+};
+
+DECLARE_MODULE(if_geneve, geneve_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(if_geneve, 1);
diff --git a/sys/net/if_geneve.h b/sys/net/if_geneve.h
new file mode 100644
index 000000000000..00cf7077fd6a
--- /dev/null
+++ b/sys/net/if_geneve.h
@@ -0,0 +1,70 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_GENEVE_H_
+#define _NET_IF_GENEVE_H_
+
+#include <sys/types.h>
+
+#ifdef _KERNEL
+struct genevehdr {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint8_t geneve_optlen:6, /* Opt Len */
+ geneve_ver:2; /* version */
+ uint8_t geneve_flags:6, /* GENEVE Flags */
+ geneve_critical:1, /* critical options present */
+ geneve_control:1; /* control packets */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t geneve_ver:2, /* version */
+ geneve_optlen:6; /* Opt Len */
+ uint8_t geneve_control:1, /* control packets */
+ geneve_critical:1, /* critical options present */
+ geneve_flags:6; /* GENEVE Flags */
+#endif
+ uint16_t geneve_proto; /* protocol type (follows Ethertypes) */
+ uint32_t geneve_vni; /* virtual network identifier */
+} __packed;
+
+struct geneveudphdr {
+ struct udphdr geneve_udp;
+ struct genevehdr geneve_hdr;
+} __packed;
+#endif /* _KERNEL */
+
+struct geneve_params {
+ uint16_t ifla_proto;
+};
+
+#define GENEVE_VNI_MAX (1 << 24)
+
+#define GENEVE_PROTO_ETHER 0x6558 /* Ethernet */
+#define GENEVE_PROTO_INHERIT 0x0 /* inherit inner layer 3 headers */
+#define GENEVE_UDPPORT 6081
+
+#endif /* _NET_IF_GENEVE_H_ */
diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c
index 272ab214a788..71bc8e1b64ce 100644
--- a/sys/net/if_gif.c
+++ b/sys/net/if_gif.c
@@ -254,27 +254,6 @@ static moduledata_t gif_mod = {
DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_VERSION(if_gif, 1);
-struct gif_list *
-gif_hashinit(void)
-{
- struct gif_list *hash;
- int i;
-
- hash = malloc(sizeof(struct gif_list) * GIF_HASH_SIZE,
- M_GIF, M_WAITOK);
- for (i = 0; i < GIF_HASH_SIZE; i++)
- CK_LIST_INIT(&hash[i]);
-
- return (hash);
-}
-
-void
-gif_hashdestroy(struct gif_list *hash)
-{
-
- free(hash, M_GIF);
-}
-
#define MTAG_GIF 1080679712
static int
gif_transmit(struct ifnet *ifp, struct mbuf *m)
diff --git a/sys/net/if_gif.h b/sys/net/if_gif.h
index c6692d3dd6bc..97a87c181a77 100644
--- a/sys/net/if_gif.h
+++ b/sys/net/if_gif.h
@@ -96,10 +96,6 @@ struct etherip_header {
#define GIF_WAIT() epoch_wait_preempt(net_epoch_preempt)
-/* Prototypes */
-struct gif_list *gif_hashinit(void);
-void gif_hashdestroy(struct gif_list *);
-
void gif_input(struct mbuf *, struct ifnet *, int, uint8_t);
int gif_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
struct route *);
diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c
index 37309260a0d3..2ff265d5d1e7 100644
--- a/sys/net/if_loop.c
+++ b/sys/net/if_loop.c
@@ -350,9 +350,13 @@ if_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen)
m_freem(m);
return (EAFNOSUPPORT);
}
- if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
- if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
- netisr_queue(isr, m); /* mbuf is free'd on failure. */
+ if (netisr_queue(isr, m) == 0) {
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
+ } else {
+ /* mbuf is free'd on failure. */
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ }
return (0);
}
diff --git a/sys/net/if_strings.h b/sys/net/if_strings.h
index a40debd30ed5..a95d697f3208 100644
--- a/sys/net/if_strings.h
+++ b/sys/net/if_strings.h
@@ -61,10 +61,14 @@
#define IFCAP_RXTLS4_NAME "RXTLS4"
#define IFCAP_RXTLS6_NAME "RXTLS6"
#define IFCAP_IPSEC_OFFLOAD_NAME "IPSEC"
+#define IFCAP_GENEVE_HWCSUM_NAME "GENEVE_HWCSUM"
+#define IFCAP_GENEVE_HWTSO_NAME "GENEVE_HWTSO"
-#define IFCAP2_RXTLS4_NAME IFCAP_RXTLS4_NAME
-#define IFCAP2_RXTLS6_NAME IFCAP_RXTLS6_NAME
-#define IFCAP2_IPSEC_OFFLOAD_NAME IFCAP_IPSEC_OFFLOAD_NAME
+#define IFCAP2_RXTLS4_NAME IFCAP_RXTLS4_NAME
+#define IFCAP2_RXTLS6_NAME IFCAP_RXTLS6_NAME
+#define IFCAP2_IPSEC_OFFLOAD_NAME IFCAP_IPSEC_OFFLOAD_NAME
+#define IFCAP2_GENEVE_HWCSUM_NAME IFCAP_GENEVE_HWCSUM_NAME
+#define IFCAP2_GENEVE_HWTSO_NAME IFCAP_GENEVE_HWTSO_NAME
#ifdef _WANT_IFCAP_BIT_NAMES
static const char *ifcap_bit_names[] = {
@@ -103,6 +107,8 @@ static const char *ifcap_bit_names[] = {
IFCAP_RXTLS4_NAME,
IFCAP_RXTLS6_NAME,
IFCAP_IPSEC_OFFLOAD_NAME,
+ IFCAP_GENEVE_HWCSUM_NAME,
+ IFCAP_GENEVE_HWTSO_NAME,
};
#ifdef IFCAP_B_SIZE
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
index f9d0b1af0f83..1fd8d57f4ee9 100644
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -190,6 +190,7 @@ struct iflib_ctx {
struct ifmedia ifc_media;
struct ifmedia *ifc_mediap;
+ struct sysctl_ctx_list ifc_sysctl_ctx;
struct sysctl_oid *ifc_sysctl_node;
uint16_t ifc_sysctl_ntxqs;
uint16_t ifc_sysctl_nrxqs;
@@ -702,6 +703,7 @@ static struct mbuf *iflib_fixup_rx(struct mbuf *m);
#endif
static __inline int iflib_completed_tx_reclaim(iflib_txq_t txq,
struct mbuf **m_defer);
+static __inline void iflib_completed_tx_reclaim_force(iflib_txq_t txq);
static SLIST_HEAD(cpu_offset_list, cpu_offset) cpu_offsets =
SLIST_HEAD_INITIALIZER(cpu_offsets);
@@ -3476,7 +3478,7 @@ iflib_ether_pad(device_t dev, struct mbuf **m_head, uint16_t min_frame_size)
}
static int
-iflib_encap(iflib_txq_t txq, struct mbuf **m_headp)
+iflib_encap(iflib_txq_t txq, struct mbuf **m_headp, int *obytes, int *opkts)
{
if_ctx_t ctx;
if_shared_ctx_t sctx;
@@ -3599,7 +3601,7 @@ defrag:
* cxgb
*/
if (__predict_false(nsegs > TXQ_AVAIL(txq))) {
- (void)iflib_completed_tx_reclaim(txq, NULL);
+ iflib_completed_tx_reclaim_force(txq);
if (__predict_false(nsegs > TXQ_AVAIL(txq))) {
txq->ift_no_desc_avail++;
bus_dmamap_unload(buf_tag, map);
@@ -3661,6 +3663,20 @@ defrag:
*/
txq->ift_pidx = pi.ipi_new_pidx;
txq->ift_npending += pi.ipi_ndescs;
+
+ /*
+ * Update packets / bytes sent
+ */
+ if (flags & IFLIB_TSO) {
+ int hlen = pi.ipi_ehdrlen + pi.ipi_ip_hlen + pi.ipi_tcp_hlen;
+ int tsolen = pi.ipi_len - hlen;
+ int nsegs = (tsolen + pi.ipi_tso_segsz - 1) / pi.ipi_tso_segsz;
+ *obytes += tsolen + nsegs * hlen;
+ *opkts += nsegs;
+ } else {
+ *obytes += pi.ipi_len;
+ *opkts += 1;
+ }
} else {
*m_headp = m_head = iflib_remove_mbuf(txq);
if (err == EFBIG) {
@@ -3788,6 +3804,20 @@ iflib_completed_tx_reclaim(iflib_txq_t txq, struct mbuf **m_defer)
return (reclaim);
}
+/*
+ * Reclaim any transmit descriptors possible, ignoring coalescing
+ */
+static __inline void
+iflib_completed_tx_reclaim_force(iflib_txq_t txq)
+{
+ int reclaim;
+
+ iflib_tx_credits_update(txq->ift_ctx, txq);
+ reclaim = DESC_RECLAIMABLE(txq);
+ if (reclaim != 0)
+ _iflib_completed_tx_reclaim(txq, NULL, reclaim);
+}
+
static struct mbuf **
_ring_peek_one(struct ifmp_ring *r, int cidx, int offset, int remaining)
{
@@ -3902,7 +3932,7 @@ iflib_txq_drain(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx)
skipped++;
continue;
}
- err = iflib_encap(txq, mp);
+ err = iflib_encap(txq, mp, &bytes_sent, &pkt_sent);
if (__predict_false(err)) {
/* no room - bail out */
if (err == ENOBUFS)
@@ -3911,10 +3941,8 @@ iflib_txq_drain(struct ifmp_ring *r, uint32_t cidx, uint32_t pidx)
/* we can't send this packet - skip it */
continue;
}
- pkt_sent++;
m = *mp;
DBG_COUNTER_INC(tx_sent);
- bytes_sent += m->m_pkthdr.len;
mcast_sent += !!(m->m_flags & M_MCAST);
if (__predict_false(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)))
@@ -5291,14 +5319,33 @@ iflib_device_register(device_t dev, void *sc, if_shared_ctx_t sctx, if_ctx_t *ct
return (0);
fail_detach:
+ CTX_UNLOCK(ctx);
+ taskqueue_drain(ctx->ifc_tq, &ctx->ifc_admin_task);
ether_ifdetach(ctx->ifc_ifp);
+ CTX_LOCK(ctx);
fail_queues:
- taskqueue_free(ctx->ifc_tq);
+ sysctl_ctx_free(&ctx->ifc_sysctl_ctx);
+ ctx->ifc_sysctl_node = NULL;
+ /*
+ * Drain without holding CTX_LOCK so _task_fn_admin can run to
+ * completion if it needs the context lock. On fail_detach we already
+ * drained above; a second drain is a no-op when the queue is empty.
+ */
+ CTX_UNLOCK(ctx);
+ taskqueue_drain(ctx->ifc_tq, &ctx->ifc_admin_task);
+ CTX_LOCK(ctx);
iflib_tqg_detach(ctx);
iflib_tx_structures_free(ctx);
iflib_rx_structures_free(ctx);
+ /*
+ * Match iflib_device_deregister: IFDI_DETACH before taskqueue_free.
+ * Avoid IFNET_WLOCK across driver detach (LinuxKPI workqueue drain).
+ */
+ IFNET_WUNLOCK();
IFDI_DETACH(ctx);
IFDI_QUEUES_FREE(ctx);
+ IFNET_WLOCK();
+ taskqueue_free(ctx->ifc_tq);
fail_intr_free:
iflib_free_intr_mem(ctx);
fail_unlock:
@@ -5332,6 +5379,9 @@ iflib_device_deregister(if_ctx_t ctx)
if_t ifp = ctx->ifc_ifp;
device_t dev = ctx->ifc_dev;
+ sysctl_ctx_free(&ctx->ifc_sysctl_ctx);
+ ctx->ifc_sysctl_node = NULL;
+
/* Make sure VLANS are not using driver */
if (if_vlantrunkinuse(ifp)) {
device_printf(dev, "Vlan in use, detach first\n");
@@ -6787,62 +6837,61 @@ iflib_add_device_sysctl_pre(if_ctx_t ctx)
{
device_t dev = iflib_get_dev(ctx);
struct sysctl_oid_list *child, *oid_list;
- struct sysctl_ctx_list *ctx_list;
struct sysctl_oid *node;
- ctx_list = device_get_sysctl_ctx(dev);
+ sysctl_ctx_init(&ctx->ifc_sysctl_ctx);
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
- ctx->ifc_sysctl_node = node = SYSCTL_ADD_NODE(ctx_list, child,
+ ctx->ifc_sysctl_node = node = SYSCTL_ADD_NODE(&ctx->ifc_sysctl_ctx, child,
OID_AUTO, "iflib", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
"IFLIB fields");
oid_list = SYSCTL_CHILDREN(node);
- SYSCTL_ADD_CONST_STRING(ctx_list, oid_list, OID_AUTO, "driver_version",
+ SYSCTL_ADD_CONST_STRING(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "driver_version",
CTLFLAG_RD, ctx->ifc_sctx->isc_driver_version, "driver version");
- SYSCTL_ADD_BOOL(ctx_list, oid_list, OID_AUTO, "simple_tx",
+ SYSCTL_ADD_BOOL(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "simple_tx",
CTLFLAG_RDTUN, &ctx->ifc_sysctl_simple_tx, 0,
"use simple tx ring");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_ntxqs",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "override_ntxqs",
CTLFLAG_RWTUN, &ctx->ifc_sysctl_ntxqs, 0,
"# of txqs to use, 0 => use default #");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_nrxqs",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "override_nrxqs",
CTLFLAG_RWTUN, &ctx->ifc_sysctl_nrxqs, 0,
"# of rxqs to use, 0 => use default #");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "override_qs_enable",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "override_qs_enable",
CTLFLAG_RWTUN, &ctx->ifc_sysctl_qs_eq_override, 0,
"permit #txq != #rxq");
- SYSCTL_ADD_INT(ctx_list, oid_list, OID_AUTO, "disable_msix",
+ SYSCTL_ADD_INT(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "disable_msix",
CTLFLAG_RWTUN, &ctx->ifc_softc_ctx.isc_disable_msix, 0,
"disable MSI-X (default 0)");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "rx_budget",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "rx_budget",
CTLFLAG_RWTUN, &ctx->ifc_sysctl_rx_budget, 0, "set the RX budget");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "tx_abdicate",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "tx_abdicate",
CTLFLAG_RWTUN, &ctx->ifc_sysctl_tx_abdicate, 0,
"cause TX to abdicate instead of running to completion");
ctx->ifc_sysctl_core_offset = CORE_OFFSET_UNSPECIFIED;
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "core_offset",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "core_offset",
CTLFLAG_RDTUN, &ctx->ifc_sysctl_core_offset, 0,
"offset to start using cores at");
- SYSCTL_ADD_U8(ctx_list, oid_list, OID_AUTO, "separate_txrx",
+ SYSCTL_ADD_U8(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "separate_txrx",
CTLFLAG_RDTUN, &ctx->ifc_sysctl_separate_txrx, 0,
"use separate cores for TX and RX");
- SYSCTL_ADD_U8(ctx_list, oid_list, OID_AUTO, "use_logical_cores",
+ SYSCTL_ADD_U8(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "use_logical_cores",
CTLFLAG_RDTUN, &ctx->ifc_sysctl_use_logical_cores, 0,
"try to make use of logical cores for TX and RX");
- SYSCTL_ADD_U16(ctx_list, oid_list, OID_AUTO, "use_extra_msix_vectors",
+ SYSCTL_ADD_U16(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "use_extra_msix_vectors",
CTLFLAG_RDTUN, &ctx->ifc_sysctl_extra_msix_vectors, 0,
"attempt to reserve the given number of extra MSI-X vectors during driver load for the creation of additional interfaces later");
- SYSCTL_ADD_INT(ctx_list, oid_list, OID_AUTO, "allocated_msix_vectors",
+ SYSCTL_ADD_INT(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "allocated_msix_vectors",
CTLFLAG_RDTUN, &ctx->ifc_softc_ctx.isc_vectors, 0,
"total # of MSI-X vectors allocated by driver");
/* XXX change for per-queue sizes */
- SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_ntxds",
+ SYSCTL_ADD_PROC(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "override_ntxds",
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, ctx,
IFLIB_NTXD_HANDLER, mp_ndesc_handler, "A",
"list of # of TX descriptors to use, 0 = use default #");
- SYSCTL_ADD_PROC(ctx_list, oid_list, OID_AUTO, "override_nrxds",
+ SYSCTL_ADD_PROC(&ctx->ifc_sysctl_ctx, oid_list, OID_AUTO, "override_nrxds",
CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, ctx,
IFLIB_NRXD_HANDLER, mp_ndesc_handler, "A",
"list of # of RX descriptors to use, 0 = use default #");
@@ -6853,9 +6902,8 @@ iflib_add_device_sysctl_post(if_ctx_t ctx)
{
if_shared_ctx_t sctx = ctx->ifc_sctx;
if_softc_ctx_t scctx = &ctx->ifc_softc_ctx;
- device_t dev = iflib_get_dev(ctx);
struct sysctl_oid_list *child;
- struct sysctl_ctx_list *ctx_list;
+ struct sysctl_ctx_list *ctx_list = &ctx->ifc_sysctl_ctx;
iflib_fl_t fl;
iflib_txq_t txq;
iflib_rxq_t rxq;
@@ -6864,7 +6912,6 @@ iflib_add_device_sysctl_post(if_ctx_t ctx)
char *qfmt;
struct sysctl_oid *queue_node, *fl_node, *node;
struct sysctl_oid_list *queue_list, *fl_list;
- ctx_list = device_get_sysctl_ctx(dev);
node = ctx->ifc_sysctl_node;
child = SYSCTL_CHILDREN(node);
@@ -7101,6 +7148,8 @@ iflib_debugnet_transmit(if_t ifp, struct mbuf *m)
if_ctx_t ctx;
iflib_txq_t txq;
int error;
+ int bytes_sent = 0;
+ int pkt_sent = 0;
ctx = if_getsoftc(ifp);
if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
@@ -7108,7 +7157,7 @@ iflib_debugnet_transmit(if_t ifp, struct mbuf *m)
return (EBUSY);
txq = &ctx->ifc_txqs[0];
- error = iflib_encap(txq, &m);
+ error = iflib_encap(txq, &m, &bytes_sent, &pkt_sent);
if (error == 0)
(void)iflib_txd_db_check(txq, true);
return (error);
@@ -7174,10 +7223,8 @@ iflib_simple_transmit(if_t ifp, struct mbuf *m)
txq = iflib_simple_select_queue(ctx, m);
mtx_lock(&txq->ift_mtx);
- error = iflib_encap(txq, &m);
+ error = iflib_encap(txq, &m, &bytes_sent, &pkt_sent);
if (error == 0) {
- pkt_sent++;
- bytes_sent += m->m_pkthdr.len;
mcast_sent += !!(m->m_flags & M_MCAST);
(void)iflib_txd_db_check(txq, true);
} else {
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 87ed701f66a7..70a391b3f07c 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2621,9 +2621,8 @@ struct pf_ifspeed_v1 {
#endif /* _KERNEL */
#ifdef _KERNEL
-LIST_HEAD(pf_ksrc_node_list, pf_ksrc_node);
struct pf_srchash {
- struct pf_ksrc_node_list nodes;
+ LIST_HEAD(pf_ksrc_node_list, pf_ksrc_node) nodes;
struct mtx lock;
};
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 6c03e621ed82..596ec9e25d1a 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -451,6 +451,7 @@ nhop_create_from_nhop(struct rib_head *rnh, const struct nhop_object *nh_orig,
nhop_free(nh);
return (error);
}
+ set_nhop_expire_from_info(nh, info);
*pnh = nhop_get_nhop(nh, &error);
@@ -491,17 +492,17 @@ finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link)
/* Allocate per-cpu packet counter */
nh->nh_pksent = counter_u64_alloc(M_NOWAIT);
if (nh->nh_pksent == NULL) {
+ FIB_NH_LOG(LOG_WARNING, nh, "counter_u64_alloc() failed");
nhop_free(nh);
RTSTAT_INC(rts_nh_alloc_failure);
- FIB_NH_LOG(LOG_WARNING, nh, "counter_u64_alloc() failed");
return (ENOMEM);
}
if (!reference_nhop_deps(nh)) {
+ FIB_NH_LOG(LOG_WARNING, nh, "interface reference failed");
counter_u64_free(nh->nh_pksent);
nhop_free(nh);
RTSTAT_INC(rts_nh_alloc_failure);
- FIB_NH_LOG(LOG_WARNING, nh, "interface reference failed");
return (EAGAIN);
}
diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h
index 9ed39d118c16..77145e4c7503 100644
--- a/sys/netinet/icmp6.h
+++ b/sys/netinet/icmp6.h
@@ -243,10 +243,6 @@ struct nd_router_advert { /* router advertisement */
#define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */
#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
-#ifdef EXPERIMENTAL
-#define ND_RA_FLAG_IPV6_ONLY 0x02 /* draft-ietf-6man-ipv6only-flag */
-#endif
-
#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
struct nd_neighbor_solicit { /* neighbor solicitation */
@@ -360,6 +356,8 @@ struct nd_opt_route_info { /* route info */
/* prefix follows */
} __packed;
+#define ND_OPT_RTI_FLAG_PRF_MASK 0x18 /* 00011000 */
+
struct nd_opt_rdnss { /* RDNSS option (RFC 6106) */
u_int8_t nd_opt_rdnss_type;
u_int8_t nd_opt_rdnss_len;
diff --git a/sys/netinet/in_gif.c b/sys/netinet/in_gif.c
index ebe7a4a62903..1910ef12d6d0 100644
--- a/sys/netinet/in_gif.c
+++ b/sys/netinet/in_gif.c
@@ -46,6 +46,7 @@
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/proc.h>
+#include <sys/hash.h>
#include <net/ethernet.h>
#include <net/if.h>
@@ -81,15 +82,17 @@ SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_VNET | CTLFLAG_RW,
*/
VNET_DEFINE_STATIC(struct gif_list *, ipv4_hashtbl) = NULL;
VNET_DEFINE_STATIC(struct gif_list *, ipv4_srchashtbl) = NULL;
+VNET_DEFINE_STATIC(u_int, ipv4_hashmask);
VNET_DEFINE_STATIC(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
#define V_ipv4_hashtbl VNET(ipv4_hashtbl)
#define V_ipv4_srchashtbl VNET(ipv4_srchashtbl)
+#define V_ipv4_hashmask VNET(ipv4_hashmask)
#define V_ipv4_list VNET(ipv4_list)
#define GIF_HASH(src, dst) (V_ipv4_hashtbl[\
- in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+ in_gif_hashval((src), (dst)) & (V_ipv4_hashmask - 1)])
#define GIF_SRCHASH(src) (V_ipv4_srchashtbl[\
- fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
+ fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (V_ipv4_hashmask - 1)])
#define GIF_HASH_SC(sc) GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
(sc)->gif_iphdr->ip_dst.s_addr)
static uint32_t
@@ -218,8 +221,15 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
break;
}
if (V_ipv4_hashtbl == NULL) {
- V_ipv4_hashtbl = gif_hashinit();
- V_ipv4_srchashtbl = gif_hashinit();
+ struct hashalloc_args ha = {
+ .size = GIF_HASH_SIZE,
+ .mtype = M_GIF,
+ .mflags = M_WAITOK,
+ .head = HASH_HEAD_CK_LIST,
+ };
+ V_ipv4_hashtbl = hashalloc(&ha);
+ V_ipv4_srchashtbl = hashalloc(&ha);
+ V_ipv4_hashmask = ha.size - 1;
}
error = in_gif_checkdup(sc, src->sin_addr.s_addr,
dst->sin_addr.s_addr);
@@ -438,9 +448,14 @@ in_gif_uninit(void)
ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
}
if (V_ipv4_hashtbl != NULL) {
- gif_hashdestroy(V_ipv4_hashtbl);
+ struct hashalloc_args ha = {
+ .size = V_ipv4_hashmask + 1,
+ .mtype = M_GIF,
+ .head = HASH_HEAD_CK_LIST,
+ };
+ hashfree(V_ipv4_hashtbl, &ha);
V_ipv4_hashtbl = NULL;
GIF_WAIT();
- gif_hashdestroy(V_ipv4_srchashtbl);
+ hashfree(V_ipv4_srchashtbl, &ha);
}
}
diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c
index 6b9bb599a475..1e7985ac01d7 100644
--- a/sys/netinet/in_mcast.c
+++ b/sys/netinet/in_mcast.c
@@ -2582,6 +2582,7 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
error = copyin(msfr.msfr_srcs, kss,
sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
if (error) {
+ IN_MULTI_UNLOCK();
free(kss, M_TEMP);
return (error);
}
@@ -2852,6 +2853,11 @@ sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
return (EINVAL);
}
+ retval = sysctl_wire_old_buffer(req,
+ sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr)));
+ if (retval)
+ return (retval);
+
ifindex = name[0];
NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(ifindex);
@@ -2862,13 +2868,6 @@ sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
return (ENOENT);
}
- retval = sysctl_wire_old_buffer(req,
- sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr)));
- if (retval) {
- NET_EPOCH_EXIT(et);
- return (retval);
- }
-
IN_MULTI_LIST_LOCK();
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index f72260834a96..22c3287b93fd 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -240,8 +240,6 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, connect_inaddr_wild,
"Allow connecting to INADDR_ANY or INADDR_BROADCAST for connect(2)");
#endif
-static void in_pcbremhash(struct inpcb *);
-
/*
* in_pcb.c: manage the Protocol Control Blocks.
*
@@ -305,7 +303,7 @@ in_pcblbgroup_find(struct inpcb *inp)
INP_HASH_LOCK_ASSERT(pcbinfo);
hdr = &pcbinfo->ipi_lbgrouphashbase[
- INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_lbgrouphashmask)];
+ INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
CK_LIST_FOREACH(grp, hdr, il_list) {
struct inpcb *inp1;
@@ -413,7 +411,7 @@ in_pcbinslbgrouphash(struct inpcb *inp, uint8_t numa_domain)
}
#endif
- idx = INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_lbgrouphashmask);
+ idx = INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask);
hdr = &pcbinfo->ipi_lbgrouphashbase[idx];
CK_LIST_FOREACH(grp, hdr, il_list) {
if (grp->il_cred->cr_prison == inp->inp_cred->cr_prison &&
@@ -474,7 +472,7 @@ in_pcbremlbgrouphash(struct inpcb *inp)
INP_HASH_WLOCK_ASSERT(pcbinfo);
hdr = &pcbinfo->ipi_lbgrouphashbase[
- INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_lbgrouphashmask)];
+ INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
CK_LIST_FOREACH(grp, hdr, il_list) {
for (i = 0; i < grp->il_inpcnt; ++i) {
if (grp->il_inp[i] != inp)
@@ -545,9 +543,6 @@ in_pcblbgroup_numa(struct inpcb *inp, int arg)
return (error);
}
-/* Make sure it is safe to use hashinit(9) on CK_LIST. */
-CTASSERT(sizeof(struct inpcbhead) == sizeof(LIST_HEAD(, inpcb)));
-
/*
* Initialize an inpcbinfo - a per-VNET instance of connections db.
*/
@@ -555,24 +550,27 @@ void
in_pcbinfo_init(struct inpcbinfo *pcbinfo, struct inpcbstorage *pcbstor,
u_int hash_nelements, u_int porthash_nelements)
{
+ struct hashalloc_args ha = {
+ .mtype = M_PCB,
+ .mflags = M_WAITOK,
+ .head = HASH_HEAD_CK_LIST,
+ };
- mtx_init(&pcbinfo->ipi_lock, pcbstor->ips_infolock_name, NULL, MTX_DEF);
mtx_init(&pcbinfo->ipi_hash_lock, pcbstor->ips_hashlock_name,
NULL, MTX_DEF);
-#ifdef VIMAGE
- pcbinfo->ipi_vnet = curvnet;
-#endif
- CK_LIST_INIT(&pcbinfo->ipi_listhead);
+ CK_LIST_INIT(&pcbinfo->ipi_list_unconn);
pcbinfo->ipi_count = 0;
- pcbinfo->ipi_hash_exact = hashinit(hash_nelements, M_PCB,
- &pcbinfo->ipi_hashmask);
- pcbinfo->ipi_hash_wild = hashinit(hash_nelements, M_PCB,
- &pcbinfo->ipi_hashmask);
- porthash_nelements = imin(porthash_nelements, IPPORT_MAX + 1);
- pcbinfo->ipi_porthashbase = hashinit(porthash_nelements, M_PCB,
- &pcbinfo->ipi_porthashmask);
- pcbinfo->ipi_lbgrouphashbase = hashinit(porthash_nelements, M_PCB,
- &pcbinfo->ipi_lbgrouphashmask);
+
+ ha.size = hash_nelements;
+ pcbinfo->ipi_hash_exact = hashalloc(&ha);
+ pcbinfo->ipi_hash_wild = hashalloc(&ha);
+ pcbinfo->ipi_hashmask = ha.size - 1;
+
+ ha.size = imin(porthash_nelements, IPPORT_MAX + 1);
+ pcbinfo->ipi_porthashbase = hashalloc(&ha);
+ pcbinfo->ipi_lbgrouphashbase = hashalloc(&ha);
+ pcbinfo->ipi_porthashmask = ha.size - 1;
+
pcbinfo->ipi_zone = pcbstor->ips_zone;
pcbinfo->ipi_smr = uma_zone_get_smr(pcbinfo->ipi_zone);
}
@@ -583,18 +581,21 @@ in_pcbinfo_init(struct inpcbinfo *pcbinfo, struct inpcbstorage *pcbstor,
void
in_pcbinfo_destroy(struct inpcbinfo *pcbinfo)
{
+ struct hashalloc_args ha = {
+ .mtype = M_PCB,
+ .head = HASH_HEAD_CK_LIST,
+ };
KASSERT(pcbinfo->ipi_count == 0,
("%s: ipi_count = %u", __func__, pcbinfo->ipi_count));
- hashdestroy(pcbinfo->ipi_hash_exact, M_PCB, pcbinfo->ipi_hashmask);
- hashdestroy(pcbinfo->ipi_hash_wild, M_PCB, pcbinfo->ipi_hashmask);
- hashdestroy(pcbinfo->ipi_porthashbase, M_PCB,
- pcbinfo->ipi_porthashmask);
- hashdestroy(pcbinfo->ipi_lbgrouphashbase, M_PCB,
- pcbinfo->ipi_lbgrouphashmask);
+ ha.size = pcbinfo->ipi_hashmask + 1;
+ hashfree(pcbinfo->ipi_hash_exact, &ha);
+ hashfree(pcbinfo->ipi_hash_wild, &ha);
+ ha.size = pcbinfo->ipi_porthashmask + 1;
+ hashfree(pcbinfo->ipi_porthashbase, &ha);
+ hashfree(pcbinfo->ipi_lbgrouphashbase, &ha);
mtx_destroy(&pcbinfo->ipi_hash_lock);
- mtx_destroy(&pcbinfo->ipi_lock);
}
/*
@@ -688,12 +689,13 @@ in_pcballoc(struct socket *so, struct inpcbinfo *pcbinfo)
*/
inp->inp_route.ro_flags = RT_LLE_CACHE;
refcount_init(&inp->inp_refcount, 1); /* Reference from socket. */
+ inp->inp_flags |= INP_UNCONNECTED;
INP_WLOCK(inp);
- INP_INFO_WLOCK(pcbinfo);
+ INP_HASH_WLOCK(pcbinfo);
pcbinfo->ipi_count++;
inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
- CK_LIST_INSERT_HEAD(&pcbinfo->ipi_listhead, inp, inp_list);
- INP_INFO_WUNLOCK(pcbinfo);
+ CK_LIST_INSERT_HEAD(&pcbinfo->ipi_list_unconn, inp, inp_unconn_list);
+ INP_HASH_WUNLOCK(pcbinfo);
so->so_pcb = inp;
return (0);
@@ -709,41 +711,6 @@ out:
#endif
}
-#ifdef INET
-int
-in_pcbbind(struct inpcb *inp, struct sockaddr_in *sin, int flags,
- struct ucred *cred)
-{
- int error;
- bool anonport;
-
- KASSERT(sin == NULL || sin->sin_family == AF_INET,
- ("%s: invalid address family for %p", __func__, sin));
- KASSERT(sin == NULL || sin->sin_len == sizeof(struct sockaddr_in),
- ("%s: invalid address length for %p", __func__, sin));
- INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
-
- if (inp->inp_lport != 0 || inp->inp_laddr.s_addr != INADDR_ANY)
- return (EINVAL);
- anonport = sin == NULL || sin->sin_port == 0;
- error = in_pcbbind_setup(inp, sin, &inp->inp_laddr.s_addr,
- &inp->inp_lport, flags, cred);
- if (error)
- return (error);
- if (__predict_false((error = in_pcbinshash(inp)) != 0)) {
- MPASS(inp->inp_socket->so_options & SO_REUSEPORT_LB);
- inp->inp_laddr.s_addr = INADDR_ANY;
- inp->inp_lport = 0;
- inp->inp_flags &= ~INP_BOUNDFIB;
- return (error);
- }
- if (anonport)
- inp->inp_flags |= INP_ANONPORT;
- return (0);
-}
-#endif
-
#if defined(INET) || defined(INET6)
/*
* Assign a local port like in_pcb_lport(), but also used with connect()
@@ -1006,9 +973,9 @@ in_pcbbind_avail(struct inpcb *inp, const struct in_addr laddr,
*
* On error, the values of *laddrp and *lportp are not changed.
*/
-int
-in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
- u_short *lportp, int flags, struct ucred *cred)
+static int
+in_pcbbind_setup_locked(struct inpcb *inp, struct sockaddr_in *sin,
+ in_addr_t *laddrp, u_short *lportp, int flags, struct ucred *cred)
{
struct socket *so = inp->inp_socket;
struct in_addr laddr;
@@ -1072,6 +1039,59 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
return (0);
}
+int
+in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
+ u_short *lportp, int flags, struct ucred *cred)
+{
+ int error;
+
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
+ error = in_pcbbind_setup_locked(inp, sin, laddrp, lportp, flags, cred);
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+
+ return (error);
+}
+
+#ifdef INET
+int
+in_pcbbind(struct inpcb *inp, struct sockaddr_in *sin, int flags,
+ struct ucred *cred)
+{
+ int error;
+ bool anonport;
+
+ KASSERT(sin == NULL || sin->sin_family == AF_INET,
+ ("%s: invalid address family for %p", __func__, sin));
+ KASSERT(sin == NULL || sin->sin_len == sizeof(struct sockaddr_in),
+ ("%s: invalid address length for %p", __func__, sin));
+ INP_WLOCK_ASSERT(inp);
+
+ if (inp->inp_lport != 0 || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ anonport = sin == NULL || sin->sin_port == 0;
+
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
+ error = in_pcbbind_setup_locked(inp, sin, &inp->inp_laddr.s_addr,
+ &inp->inp_lport, flags, cred);
+ if (error) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+ return (error);
+ }
+ if (__predict_false((error = in_pcbinshash(inp)) != 0)) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+ MPASS(inp->inp_socket->so_options & SO_REUSEPORT_LB);
+ inp->inp_laddr.s_addr = INADDR_ANY;
+ inp->inp_lport = 0;
+ inp->inp_flags &= ~INP_BOUNDFIB;
+ return (error);
+ }
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+ if (anonport)
+ inp->inp_flags |= INP_ANONPORT;
+ return (0);
+}
+#endif
+
/*
* Connect from a socket to a specified address.
* Both address and port must be specified in argument sin.
@@ -1086,8 +1106,8 @@ in_pcbconnect(struct inpcb *inp, struct sockaddr_in *sin, struct ucred *cred)
int error;
bool anonport;
+ NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
KASSERT(in_nullhost(inp->inp_faddr),
("%s: inp is already connected", __func__));
KASSERT(sin->sin_family == AF_INET,
@@ -1124,10 +1144,13 @@ in_pcbconnect(struct inpcb *inp, struct sockaddr_in *sin, struct ucred *cred)
} else
faddr = sin->sin_addr;
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
if (in_nullhost(inp->inp_laddr)) {
error = in_pcbladdr(inp, &faddr, &laddr, cred);
- if (error)
+ if (__predict_false(error)) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
return (error);
+ }
} else
laddr = inp->inp_laddr;
@@ -1144,28 +1167,32 @@ in_pcbconnect(struct inpcb *inp, struct sockaddr_in *sin, struct ucred *cred)
error = in_pcb_lport_dest(inp, (struct sockaddr *)&lsin,
&lport, (struct sockaddr *)&fsin, sin->sin_port, cred,
INPLOOKUP_WILDCARD);
- if (error)
+ if (__predict_false(error)) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
return (error);
+ }
} else if (in_pcblookup_hash_locked(inp->inp_pcbinfo, faddr,
sin->sin_port, laddr, inp->inp_lport, 0, M_NODOM, RT_ALL_FIBS) !=
- NULL)
+ NULL) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
return (EADDRINUSE);
- else
+ } else
lport = inp->inp_lport;
MPASS(!in_nullhost(inp->inp_laddr) || inp->inp_lport != 0 ||
- !(inp->inp_flags & INP_INHASHLIST));
+ (inp->inp_flags & INP_UNCONNECTED));
inp->inp_faddr = faddr;
inp->inp_fport = sin->sin_port;
inp->inp_laddr = laddr;
inp->inp_lport = lport;
- if ((inp->inp_flags & INP_INHASHLIST) == 0) {
+ if (inp->inp_flags & INP_UNCONNECTED) {
error = in_pcbinshash(inp);
MPASS(error == 0);
} else
in_pcbrehash(inp);
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
if (V_fib_hash_outbound) {
uint32_t hash_val, hash_type;
@@ -1419,17 +1446,25 @@ in_pcbdisconnect(struct inpcb *inp)
{
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
KASSERT(inp->inp_smr == SMR_SEQ_INVALID,
("%s: inp %p was already disconnected", __func__, inp));
- in_pcbremhash_locked(inp);
+ if (inp->inp_flags & INP_UNCONNECTED)
+ return;
- /* See the comment in in_pcbinshash(). */
- inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
- inp->inp_laddr.s_addr = INADDR_ANY;
- inp->inp_faddr.s_addr = INADDR_ANY;
- inp->inp_fport = 0;
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
+ in_pcbremhash(inp);
+ CK_LIST_INSERT_HEAD(&inp->inp_pcbinfo->ipi_list_unconn, inp,
+ inp_unconn_list);
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+ inp->inp_flags |= INP_UNCONNECTED;
+
+ if ((inp->inp_socket->so_proto->pr_flags & PR_CONNREQUIRED) == 0) {
+ /* See the comment in in_pcbinshash(). */
+ inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+ }
}
#endif /* INET */
@@ -1529,11 +1564,11 @@ inp_smr_lock(struct inpcb *inp, const inp_lookup_t lock)
{
/*
- * in_pcblookup() family of functions ignore not only freed entries,
- * that may be found due to lockless access to the hash, but dropped
- * entries, too.
+ * in_pcblookup() family of functions shall ignore not onlu pcbs that
+ * had been freed that may be found due to lockless access to the hash,
+ * but also pcbs that were removed from the hash, but are still around.
*/
- return (_inp_smr_lock(inp, lock, INP_FREED | INP_DROPPED));
+ return (_inp_smr_lock(inp, lock, INP_FREED | INP_UNCONNECTED));
}
/*
@@ -1544,8 +1579,17 @@ inp_smr_lock(struct inpcb *inp, const inp_lookup_t lock)
*
* - Iterator can have either write-lock or read-lock semantics, that can not
* be changed later.
- * - Iterator can iterate either over all pcbs list (INP_ALL_LIST), or through
- * a single hash slot. Note: only rip_input() does the latter.
+ * - Iterator has three modes of operation, defined by value of .hash member
+ * on the first call:
+ * - .hash = INP_ALL_LIST: the iterator will go through the unconnected
+ * list, then all wildcard hash slots and then all exact hash slots.
+ * - .hash = INP_UNCONN_LIST: the iterator will go through the list of
+ * unconnected pcbs only.
+ * - .hash initialized with an arbitrary positive value: iterator will go
+ * through this exact hash slot only.
+ * Note: only rip_input() and sysctl_setsockopt() use the latter.
+ * The interface may be extended for iteration over single wildcard hash
+ * slot, but there is no use case for that today.
* - Iterator may have optional bool matching function. The matching function
* will be executed for each inpcb in the SMR context, so it can not acquire
* locks and can safely access only immutable fields of inpcb.
@@ -1563,49 +1607,72 @@ inp_smr_lock(struct inpcb *inp, const inp_lookup_t lock)
* - Removed entries won't stop traversal as long as they are not added to
* a different list. This is violated by in_pcbrehash().
*/
-#define II_LIST_FIRST(ipi, hash) \
- (((hash) == INP_ALL_LIST) ? \
- CK_LIST_FIRST(&(ipi)->ipi_listhead) : \
- CK_LIST_FIRST(&(ipi)->ipi_hash_exact[(hash)]))
-#define II_LIST_NEXT(inp, hash) \
- (((hash) == INP_ALL_LIST) ? \
- CK_LIST_NEXT((inp), inp_list) : \
- CK_LIST_NEXT((inp), inp_hash_exact))
-#define II_LOCK_ASSERT(inp, lock) \
- rw_assert(&(inp)->inp_lock, \
- (lock) == INPLOOKUP_RLOCKPCB ? RA_RLOCKED : RA_WLOCKED )
+static inline struct inpcb *
+ii_list_first(const struct inpcb_iterator *ii)
+{
+ const struct inpcbinfo *ipi = ii->ipi;
+ const int hash = ii->hash;
+
+ if (hash < 0)
+ return (CK_LIST_FIRST(&ipi->ipi_list_unconn));
+ else if (hash <= ipi->ipi_hashmask)
+ return (CK_LIST_FIRST(&ipi->ipi_hash_wild[hash]));
+ else
+ return (CK_LIST_FIRST(
+ &ipi->ipi_hash_exact[hash - ipi->ipi_hashmask - 1]));
+}
+
+static inline struct inpcb *
+ii_list_next(const struct inpcb_iterator *ii, struct inpcb *inp)
+{
+ if (ii->hash < 0)
+ return (CK_LIST_NEXT(inp, inp_unconn_list));
+ else if (ii->hash <= ii->ipi->ipi_hashmask)
+ return (CK_LIST_NEXT(inp, inp_hash_wild));
+ else
+ return (CK_LIST_NEXT(inp, inp_hash_exact));
+}
+
struct inpcb *
inp_next(struct inpcb_iterator *ii)
{
const struct inpcbinfo *ipi = ii->ipi;
+ const int hashmax = (ipi->ipi_hashmask + 1) * 2;
inp_match_t *match = ii->match;
void *ctx = ii->ctx;
inp_lookup_t lock = ii->lock;
- int hash = ii->hash;
struct inpcb *inp;
if (ii->inp == NULL) { /* First call. */
+ if ((ii->hash = ii->mode) >= 0) {
+ /* Targeted iterators support only the exact hash. */
+ MPASS(ii->hash <= ipi->ipi_hashmask);
+ ii->hash += ipi->ipi_hashmask + 1;
+ }
smr_enter(ipi->ipi_smr);
- /* This is unrolled CK_LIST_FOREACH(). */
- for (inp = II_LIST_FIRST(ipi, hash);
+next_first:
+ /* This is unrolled CK_LIST_FOREACH() over different headers. */
+ for (inp = ii_list_first(ii);
inp != NULL;
- inp = II_LIST_NEXT(inp, hash)) {
+ inp = ii_list_next(ii, inp)) {
if (match != NULL && (match)(inp, ctx) == false)
continue;
if (__predict_true(_inp_smr_lock(inp, lock, INP_FREED)))
break;
else {
smr_enter(ipi->ipi_smr);
- MPASS(inp != II_LIST_FIRST(ipi, hash));
- inp = II_LIST_FIRST(ipi, hash);
+ MPASS(inp != ii_list_first(ii));
+ inp = ii_list_first(ii);
if (inp == NULL)
break;
}
}
- if (inp == NULL)
+ if (inp == NULL) {
+ if (ii->mode == INP_ALL_LIST && ++ii->hash < hashmax)
+ goto next_first;
smr_exit(ipi->ipi_smr);
- else
+ } else
ii->inp = inp;
return (inp);
@@ -1615,10 +1682,16 @@ inp_next(struct inpcb_iterator *ii)
smr_enter(ipi->ipi_smr);
restart:
inp = ii->inp;
- II_LOCK_ASSERT(inp, lock);
+ rw_assert(&inp->inp_lock,
+ lock == INPLOOKUP_RLOCKPCB ? RA_RLOCKED : RA_WLOCKED);
next:
- inp = II_LIST_NEXT(inp, hash);
+ inp = ii_list_next(ii, inp);
if (inp == NULL) {
+ if (ii->mode == INP_ALL_LIST && ++ii->hash < hashmax) {
+ inp_unlock(ii->inp, lock);
+ ii->inp = NULL;
+ goto next_first;
+ }
smr_exit(ipi->ipi_smr);
goto found;
}
@@ -1789,13 +1862,14 @@ in_pcbfree(struct inpcb *inp)
* from the hash without acquiring inpcb lock, they rely on the hash
* lock, thus in_pcbremhash() should be the first action.
*/
- if (inp->inp_flags & INP_INHASHLIST)
+ INP_HASH_WLOCK(pcbinfo);
+ if (inp->inp_flags & INP_UNCONNECTED)
+ CK_LIST_REMOVE(inp, inp_unconn_list);
+ else
in_pcbremhash(inp);
- INP_INFO_WLOCK(pcbinfo);
inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
pcbinfo->ipi_count--;
- CK_LIST_REMOVE(inp, inp_list);
- INP_INFO_WUNLOCK(pcbinfo);
+ INP_HASH_WUNLOCK(pcbinfo);
#ifdef RATELIMIT
if (inp->inp_snd_tag != NULL)
@@ -1853,31 +1927,6 @@ inpcb_fini(void *mem, int size)
INP_LOCK_DESTROY(inp);
}
-/*
- * in_pcbdrop() removes an inpcb from hashed lists, releasing its address and
- * port reservation, and preventing it from being returned by inpcb lookups.
- *
- * It is used by TCP to mark an inpcb as unused and avoid future packet
- * delivery or event notification when a socket remains open but TCP has
- * closed. This might occur as a result of a shutdown()-initiated TCP close
- * or a RST on the wire, and allows the port binding to be reused while still
- * maintaining the invariant that so_pcb always points to a valid inpcb until
- * in_pcbdetach().
- *
- * XXXRW: Possibly in_pcbdrop() should also prevent future notifications by
- * in_pcbpurgeif0()?
- */
-void
-in_pcbdrop(struct inpcb *inp)
-{
-
- INP_WLOCK_ASSERT(inp);
-
- inp->inp_flags |= INP_DROPPED;
- if (inp->inp_flags & INP_INHASHLIST)
- in_pcbremhash(inp);
-}
-
#ifdef INET
/*
* Common routines to return the socket addresses associated with inpcbs.
@@ -2107,7 +2156,7 @@ in_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
NET_EPOCH_ASSERT();
hdr = &pcbinfo->ipi_lbgrouphashbase[
- INP_PCBPORTHASH(lport, pcbinfo->ipi_lbgrouphashmask)];
+ INP_PCBPORTHASH(lport, pcbinfo->ipi_porthashmask)];
/*
* Search for an LB group match based on the following criteria:
@@ -2638,8 +2687,7 @@ in_pcbinshash(struct inpcb *inp)
INP_WLOCK_ASSERT(inp);
INP_HASH_WLOCK_ASSERT(pcbinfo);
- KASSERT((inp->inp_flags & INP_INHASHLIST) == 0,
- ("in_pcbinshash: INP_INHASHLIST"));
+ MPASS(inp->inp_flags & INP_UNCONNECTED);
#ifdef INET6
if (inp->inp_vflag & INP_IPV6) {
@@ -2685,6 +2733,8 @@ in_pcbinshash(struct inpcb *inp)
inp->inp_smr = SMR_SEQ_INVALID;
}
+ CK_LIST_REMOVE(inp, inp_unconn_list);
+
if (connected)
CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash_exact);
else {
@@ -2696,18 +2746,18 @@ in_pcbinshash(struct inpcb *inp)
_in_pcbinshash_wild(pcbhash, inp);
}
CK_LIST_INSERT_HEAD(pcbporthash, inp, inp_portlist);
- inp->inp_flags |= INP_INHASHLIST;
+ inp->inp_flags &= ~INP_UNCONNECTED;
return (0);
}
void
-in_pcbremhash_locked(struct inpcb *inp)
+in_pcbremhash(struct inpcb *inp)
{
INP_WLOCK_ASSERT(inp);
INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
- MPASS(inp->inp_flags & INP_INHASHLIST);
+ MPASS(!(inp->inp_flags & INP_UNCONNECTED));
if ((inp->inp_flags & INP_INLBGROUP) != 0)
in_pcbremlbgrouphash(inp);
@@ -2726,15 +2776,6 @@ in_pcbremhash_locked(struct inpcb *inp)
CK_LIST_REMOVE(inp, inp_hash_exact);
}
CK_LIST_REMOVE(inp, inp_portlist);
- inp->inp_flags &= ~INP_INHASHLIST;
-}
-
-static void
-in_pcbremhash(struct inpcb *inp)
-{
- INP_HASH_WLOCK(inp->inp_pcbinfo);
- in_pcbremhash_locked(inp);
- INP_HASH_WUNLOCK(inp->inp_pcbinfo);
}
/*
@@ -2753,8 +2794,7 @@ in_pcbrehash(struct inpcb *inp)
INP_WLOCK_ASSERT(inp);
INP_HASH_WLOCK_ASSERT(pcbinfo);
- KASSERT(inp->inp_flags & INP_INHASHLIST,
- ("%s: !INP_INHASHLIST", __func__));
+ MPASS(!(inp->inp_flags & INP_UNCONNECTED));
KASSERT(inp->inp_smr == SMR_SEQ_INVALID,
("%s: inp was disconnected", __func__));
@@ -2779,20 +2819,53 @@ in_pcbrehash(struct inpcb *inp)
* When rehashing, the caller must ensure that either the new or the old
* foreign address was unspecified.
*/
- if (connected)
- CK_LIST_REMOVE(inp, inp_hash_wild);
- else
- CK_LIST_REMOVE(inp, inp_hash_exact);
-
if (connected) {
+ CK_LIST_REMOVE(inp, inp_hash_wild);
head = &pcbinfo->ipi_hash_exact[hash];
CK_LIST_INSERT_HEAD(head, inp, inp_hash_exact);
} else {
+ CK_LIST_REMOVE(inp, inp_hash_exact);
head = &pcbinfo->ipi_hash_wild[hash];
CK_LIST_INSERT_HEAD(head, inp, inp_hash_wild);
}
}
+void
+ripcb_connect(struct inpcb *inp)
+{
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ uint32_t hash;
+
+ INP_WLOCK_ASSERT(inp);
+ MPASS(inp->inp_flags & INP_UNCONNECTED);
+
+ hash = RIPCB_HASH(inp) & pcbinfo->ipi_hashmask;
+
+ INP_HASH_WLOCK(pcbinfo);
+ CK_LIST_REMOVE(inp, inp_unconn_list);
+ CK_LIST_INSERT_HEAD(&pcbinfo->ipi_hash_exact[hash], inp,
+ inp_hash_exact);
+ INP_HASH_WUNLOCK(pcbinfo);
+ inp->inp_flags &= ~INP_UNCONNECTED;
+}
+
+void
+ripcb_disconnect(struct inpcb *inp)
+{
+ struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+
+ INP_WLOCK_ASSERT(inp);
+
+ if (inp->inp_flags & INP_UNCONNECTED)
+ return;
+
+ INP_HASH_WLOCK(pcbinfo);
+ CK_LIST_REMOVE(inp, inp_hash_exact);
+ CK_LIST_INSERT_HEAD(&pcbinfo->ipi_list_unconn, inp, inp_unconn_list);
+ INP_HASH_WUNLOCK(pcbinfo);
+ inp->inp_flags |= INP_UNCONNECTED;
+}
+
/*
* Check for alternatives when higher level complains
* about service problems. For now, invalidate cached
@@ -2996,7 +3069,13 @@ sysctl_setsockopt(SYSCTL_HANDLER_ARGS, struct inpcbinfo *pcbinfo,
}
while ((inp = inp_next(&inpi)) != NULL)
if (inp->inp_gencnt == params->sop_id) {
- if (inp->inp_flags & INP_DROPPED) {
+ /*
+ * XXXGL
+ * 1) the inp_next() that ignores INP_UNCONNECTED needs
+ * to be generally supported.
+ * 2) Why do we ECONNRESET instead of continueing?
+ */
+ if (inp->inp_flags & INP_UNCONNECTED) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
@@ -3225,7 +3304,7 @@ in_pcbattach_txrtlmt(struct inpcb *inp, struct ifnet *ifp,
* down, allocating a new send tag is not allowed. Else send
* tags may leak.
*/
- if (*st != NULL || (inp->inp_flags & INP_DROPPED) != 0)
+ if (*st != NULL || (inp->inp_flags & INP_UNCONNECTED))
return (EINVAL);
error = m_snd_tag_alloc(ifp, &params, st);
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 6f842f64775e..fb52f59ff725 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -181,7 +181,7 @@ struct xinpgen {
#define INP_RECVTTL 0x00000400 /* receive incoming IP TTL */
#define INP_DONTFRAG 0x00000800 /* don't fragment packet */
#define INP_BINDANY 0x00001000 /* allow bind to any address */
-#define INP_INHASHLIST 0x00002000 /* in_pcbinshash() has been called */
+/* available 0x00002000 */
#define INP_RECVTOS 0x00004000 /* receive incoming IP TOS */
#define IN6P_IPV6_V6ONLY 0x00008000 /* restrict AF_INET6 socket for v6 */
#define IN6P_PKTINFO 0x00010000 /* receive IP6 dst and I/F */
@@ -194,7 +194,7 @@ struct xinpgen {
#define IN6P_AUTOFLOWLABEL 0x00800000 /* attach flowlabel automatically */
/* INP_INLBGROUP 0x01000000 private to in_pcb.c */
#define INP_ONESBCAST 0x02000000 /* send all-ones broadcast */
-#define INP_DROPPED 0x04000000 /* protocol drop flag */
+/* INP_UNCONNECTED 0x04000000 private to in_pcb.c/in6_pcb.c */
#define INP_SOCKREF 0x08000000 /* strong socket reference */
#define INP_RESERVED_0 0x10000000 /* reserved field */
#define INP_BOUNDFIB 0x20000000 /* Bound to a specific FIB. */
@@ -213,10 +213,10 @@ struct xinpgen {
"\1INP_RECVOPTS\2INP_RECVRETOPTS\3INP_RECVDSTADDR\4INP_HDRINCL" \
"\5INP_HIGHPORT\6INP_LOWPORT\7INP_ANONPORT\10INP_RECVIF" \
"\11INP_MTUDISC\12INP_FREED\13INP_RECVTTL\14INP_DONTFRAG" \
- "\15INP_BINDANY\16INP_INHASHLIST\17INP_RECVTOS\20IN6P_IPV6_V6ONLY" \
+ "\15INP_BINDANY\17INP_RECVTOS\20IN6P_IPV6_V6ONLY" \
"\21IN6P_PKTINFO\22IN6P_HOPLIMIT\23IN6P_HOPOPTS\24IN6P_DSTOPTS" \
"\25IN6P_RTHDR\26IN6P_RTHDRDSTOPTS\27IN6P_TCLASS\30IN6P_AUTOFLOWLABEL" \
- "\31INP_INLBGROUP\32INP_ONESBCAST\33INP_DROPPED\34INP_SOCKREF" \
+ "\31INP_INLBGROUP\32INP_ONESBCAST\33INP_UNCONNECTED\34INP_SOCKREF" \
"\35INP_RESERVED_0\36INP_BOUNDFIB\37IN6P_RFC2292\40IN6P_MTU"
/*
@@ -299,7 +299,6 @@ struct xktls_session {
#include <net/route.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
-#include <net/vnet.h>
#include <vm/uma.h>
#include <sys/ck.h>
@@ -321,10 +320,15 @@ CK_LIST_HEAD(inpcblbgrouphead, inpcblbgroup);
* Almost all fields of struct inpcb are static after creation or protected by
* a per-inpcb rwlock, inp_lock.
*
- * A inpcb database is indexed by addresses/ports hash as well as list of
- * all pcbs that belong to a certain proto. Database lookups or list traversals
- * are be performed inside SMR section. Once desired PCB is found its own
- * lock is to be obtained and SMR section exited.
+ * A inpcb database consists of two hash tables: one for connected pcbs and the
+ * other for wildcard-bound pcbs. The newborn pcbs reside on the unconnected
+ * list. Although a pcb can be on either of these three lists, we can't share
+ * the linkage pointers because unlocked readers might be iterating over them.
+ * The only thing that can be unionized is the load-balance table and exact
+ * hash, as a pcb can never participate in both tables through its entire life
+ * time. Database lookups or list traversals are to be performed inside SMR
+ * section. Once desired PCB is found its own lock is to be obtained and SMR
+ * section exited.
*
* Key:
* (c) - Constant after initialization
@@ -350,14 +354,13 @@ struct icmp6_filter;
struct inpcbpolicy;
struct m_snd_tag;
struct inpcb {
- /* Cache line #1 (amd64) */
union {
- CK_LIST_ENTRY(inpcb) inp_hash_exact; /* hash table linkage */
- LIST_ENTRY(inpcb) inp_lbgroup_list; /* lb group list */
+ CK_LIST_ENTRY(inpcb) inp_hash_exact;
+ LIST_ENTRY(inpcb) inp_lbgroup_list;
};
- CK_LIST_ENTRY(inpcb) inp_hash_wild; /* hash table linkage */
+ CK_LIST_ENTRY(inpcb) inp_hash_wild;
+ CK_LIST_ENTRY(inpcb) inp_unconn_list;
struct rwlock inp_lock;
- /* Cache line #2 (amd64) */
#define inp_start_zero inp_refcount
#define inp_zero_size (sizeof(struct inpcb) - \
offsetof(struct inpcb, inp_start_zero))
@@ -412,40 +415,29 @@ struct inpcb {
struct route inp_route;
struct route_in6 inp_route6;
};
- CK_LIST_ENTRY(inpcb) inp_list; /* (r:e/w:p) all PCBs for proto */
};
-#define inp_vnet inp_pcbinfo->ipi_vnet
-
/*
* Per-VNET pcb database for each high-level protocol (UDP, TCP, ...) in both
* IPv4 and IPv6.
*
* The pcbs are protected with SMR section and thus all lists in inpcbinfo
- * are CK-lists. Locking is required to insert a pcb into database. Two
- * locks are provided: one for the hash and one for the global list of pcbs,
- * as well as overall count and generation count.
+ * are CK-lists. Locking is required to insert a pcb into database.
*
* Locking key:
*
* (c) Constant or nearly constant after initialisation
* (e) Protected by SMR section
- * (g) Locked by ipi_lock
* (h) Locked by ipi_hash_lock
*/
struct inpcbinfo {
- /*
- * Global lock protecting inpcb list modification
- */
- struct mtx ipi_lock;
- struct inpcbhead ipi_listhead; /* (r:e/w:g) */
- u_int ipi_count; /* (g) */
+ u_int ipi_count; /* (h) */
/*
* Generation count -- incremented each time a connection is allocated
* or freed.
*/
- u_quad_t ipi_gencnt; /* (g) */
+ uint64_t ipi_gencnt; /* (h) */
/*
* Fields associated with port lookup and allocation.
@@ -467,27 +459,22 @@ struct inpcbinfo {
* address, and "wild" holds the rest.
*/
struct mtx ipi_hash_lock;
+ struct inpcbhead ipi_list_unconn; /* (r:e/w:h) */
struct inpcbhead *ipi_hash_exact; /* (r:e/w:h) */
struct inpcbhead *ipi_hash_wild; /* (r:e/w:h) */
u_long ipi_hashmask; /* (c) */
+ u_long ipi_porthashmask; /* (h) */
/*
* Global hash of inpcbs, hashed by only local port number.
*/
struct inpcbhead *ipi_porthashbase; /* (h) */
- u_long ipi_porthashmask; /* (h) */
/*
* Load balance groups used for the SO_REUSEPORT_LB option,
* hashed by local port.
*/
struct inpcblbgrouphead *ipi_lbgrouphashbase; /* (r:e/w:h) */
- u_long ipi_lbgrouphashmask; /* (h) */
-
- /*
- * Pointer to network stack instance
- */
- struct vnet *ipi_vnet; /* (c) */
};
/*
@@ -499,11 +486,10 @@ struct inpcbstorage {
uma_init ips_pcbinit;
size_t ips_size;
const char * ips_zone_name;
- const char * ips_infolock_name;
const char * ips_hashlock_name;
};
-#define INPCBSTORAGE_DEFINE(prot, ppcb, lname, zname, iname, hname) \
+#define INPCBSTORAGE_DEFINE(prot, ppcb, lname, zname, hname) \
static int \
prot##_inpcb_init(void *mem, int size __unused, int flags __unused) \
{ \
@@ -516,7 +502,6 @@ static struct inpcbstorage prot = { \
.ips_size = sizeof(struct ppcb), \
.ips_pcbinit = prot##_inpcb_init, \
.ips_zone_name = zname, \
- .ips_infolock_name = iname, \
.ips_hashlock_name = hname, \
}; \
SYSINIT(prot##_inpcbstorage_init, SI_SUB_PROTO_DOMAIN, \
@@ -565,15 +550,6 @@ struct socket *
void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
uint32_t *faddr, uint16_t *fp);
-#define INP_INFO_WLOCK(ipi) mtx_lock(&(ipi)->ipi_lock)
-#define INP_INFO_WLOCKED(ipi) mtx_owned(&(ipi)->ipi_lock)
-#define INP_INFO_WUNLOCK(ipi) mtx_unlock(&(ipi)->ipi_lock)
-#define INP_INFO_LOCK_ASSERT(ipi) MPASS(SMR_ENTERED((ipi)->ipi_smr) || \
- mtx_owned(&(ipi)->ipi_lock))
-#define INP_INFO_WLOCK_ASSERT(ipi) mtx_assert(&(ipi)->ipi_lock, MA_OWNED)
-#define INP_INFO_WUNLOCK_ASSERT(ipi) \
- mtx_assert(&(ipi)->ipi_lock, MA_NOTOWNED)
-
#define INP_HASH_WLOCK(ipi) mtx_lock(&(ipi)->ipi_hash_lock)
#define INP_HASH_WUNLOCK(ipi) mtx_unlock(&(ipi)->ipi_hash_lock)
#define INP_HASH_LOCK_ASSERT(ipi) MPASS(SMR_ENTERED((ipi)->ipi_smr) || \
@@ -581,6 +557,9 @@ void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
#define INP_HASH_WLOCK_ASSERT(ipi) mtx_assert(&(ipi)->ipi_hash_lock, \
MA_OWNED)
+VNET_DECLARE(uint32_t, in_pcbhashseed);
+#define V_in_pcbhashseed VNET(in_pcbhashseed)
+
/*
* Wildcard matching hash is not just a microoptimisation! The hash for
* wildcard IPv4 and wildcard IPv6 must be the same, otherwise AF_INET6
@@ -612,6 +591,18 @@ void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
#define INP_PCBPORTHASH(lport, mask) (ntohs((lport)) & (mask))
+#if defined(INET) && defined(INET6)
+#define RIPCB_HASH(inp) (((inp)->inp_vflag & INP_IPV6) ? \
+ IN6_ADDR_JHASH32(&(inp)->in6p_faddr) : \
+ IN_ADDR_JHASH32(&(inp)->inp_faddr))
+#elif defined(INET6)
+#define RIPCB_HASH(inp) \
+ IN6_ADDR_JHASH32(&(inp)->in6p_faddr)
+#else
+#define RIPCB_HASH(inp) \
+ IN_ADDR_JHASH32(&(inp)->inp_faddr)
+#endif
+
/*
* Flags passed to in_pcblookup*(), inp_smr_lock() and inp_next().
*/
@@ -666,7 +657,6 @@ int in_pcbbind_setup(struct inpcb *, struct sockaddr_in *, in_addr_t *,
u_short *, int, struct ucred *);
int in_pcbconnect(struct inpcb *, struct sockaddr_in *, struct ucred *);
void in_pcbdisconnect(struct inpcb *);
-void in_pcbdrop(struct inpcb *);
void in_pcbfree(struct inpcb *);
int in_pcbladdr(const struct inpcb *, struct in_addr *, struct in_addr *,
struct ucred *);
@@ -683,20 +673,35 @@ bool in_pcbrele(struct inpcb *, inp_lookup_t);
bool in_pcbrele_rlocked(struct inpcb *);
bool in_pcbrele_wlocked(struct inpcb *);
bool in_pcbrele_rlock(struct inpcb *inp);
+void ripcb_connect(struct inpcb *);
+void ripcb_disconnect(struct inpcb *);
+
#ifdef _SYS_SOCKETVAR_H_
void in_pcbtoxinpcb(const struct inpcb *, struct xinpcb *);
int sysctl_setsockopt(SYSCTL_HANDLER_ARGS, struct inpcbinfo *pcbinfo,
int (*ctloutput_set)(struct inpcb *, struct sockopt *));
#endif
+/*
+ * struct inpcb_iterator is located on the stack of a function that uses
+ * inp_next(). The caller shall initialize the const members before first
+ * invocation of inp_next(). After that, until the iterator finishes the
+ * caller is supposed to only read 'inp' until it reads NULL. Some members
+ * have constness commented out for convenience of callers, that may reuse
+ * the iterator after it finishes.
+ * (c) - caller
+ * (n) - inp_next()
+ */
typedef bool inp_match_t(const struct inpcb *, void *);
struct inpcb_iterator {
const struct inpcbinfo *ipi;
- struct inpcb *inp;
- inp_match_t *match;
- void *ctx;
- int hash;
+ struct inpcb *inp; /* c:r, n:rw */
+ /* const */ inp_match_t *match;
+ /* const */ void *ctx;
+ int hash; /* n:rw */
+ /* const */ int mode;
#define INP_ALL_LIST -1
+#define INP_UNCONN_LIST -2
const inp_lookup_t lock;
};
@@ -705,7 +710,7 @@ struct inpcb_iterator {
{ \
.ipi = (_ipi), \
.lock = (_lock), \
- .hash = INP_ALL_LIST, \
+ .mode = INP_ALL_LIST, \
.match = (_match), \
.ctx = (_ctx), \
}
@@ -713,7 +718,7 @@ struct inpcb_iterator {
{ \
.ipi = (_ipi), \
.lock = (_lock), \
- .hash = INP_ALL_LIST, \
+ .mode = INP_ALL_LIST, \
}
struct inpcb *inp_next(struct inpcb_iterator *);
diff --git a/sys/netinet/in_pcb_var.h b/sys/netinet/in_pcb_var.h
index 7e8a1626ab40..8b005712de27 100644
--- a/sys/netinet/in_pcb_var.h
+++ b/sys/netinet/in_pcb_var.h
@@ -41,8 +41,7 @@
* Definitions shared between netinet/in_pcb.c and netinet6/in6_pcb.c
*/
-VNET_DECLARE(uint32_t, in_pcbhashseed);
-#define V_in_pcbhashseed VNET(in_pcbhashseed)
+#define INP_UNCONNECTED 0x04000000 /* Not inserted into hashes. */
void inp_lock(struct inpcb *inp, const inp_lookup_t lock);
void inp_unlock(struct inpcb *inp, const inp_lookup_t lock);
@@ -57,7 +56,7 @@ struct inpcb *in_pcblookup_local(struct inpcbinfo *, struct in_addr, u_short,
int, int, struct ucred *);
int in_pcbinshash(struct inpcb *);
void in_pcbrehash(struct inpcb *);
-void in_pcbremhash_locked(struct inpcb *);
+void in_pcbremhash(struct inpcb *);
/*
* Load balance groups used for the SO_REUSEPORT_LB socket option. Each group
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
index 32823c6c4a87..9a97c1a19525 100644
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -1216,7 +1216,7 @@ carp_send_ad_locked(struct carp_softc *sc)
ch.carp_vhid = sc->sc_vhid;
ch.carp_advbase = sc->sc_advbase;
ch.carp_advskew = advskew;
- ch.carp_authlen = 7; /* XXX DEFINE */
+ ch.carp_authlen = CARP_AUTHLEN;
ch.carp_pad1 = 0; /* must be zero */
ch.carp_cksum = 0;
diff --git a/sys/netinet/ip_carp.h b/sys/netinet/ip_carp.h
index 8d82286feb7d..97040403b41e 100644
--- a/sys/netinet/ip_carp.h
+++ b/sys/netinet/ip_carp.h
@@ -81,6 +81,12 @@ struct carp_header {
CTASSERT(sizeof(struct carp_header) == 36);
/*
+ * CARP authentication length in 32-bit chunks:
+ * counter[2] (8 bytes) + SHA1 HMAC (20 bytes) = 28 bytes = 7 chunks.
+ */
+#define CARP_AUTHLEN 7
+
+/*
* The VRRPv3 header layout is as follows:
* See RFC9568, 5.1. VRRP Packet Format
*
diff --git a/sys/netinet/ip_fastfwd.c b/sys/netinet/ip_fastfwd.c
index 6001ce781bc8..d95cca490a5d 100644
--- a/sys/netinet/ip_fastfwd.c
+++ b/sys/netinet/ip_fastfwd.c
@@ -293,7 +293,7 @@ ip_tryforward(struct mbuf *m)
/*
* Is it for a local address on this host?
*/
- if (in_localip(ip->ip_dst))
+ if (in_localip_fib(ip->ip_dst, M_GETFIB(m)))
return m;
IPSTAT_INC(ips_total);
@@ -328,7 +328,7 @@ ip_tryforward(struct mbuf *m)
/*
* Is it now for a local address on this host?
*/
- if (in_localip(dest))
+ if (in_localip_fib(dest, M_GETFIB(m)))
goto forwardlocal;
/*
* Go on with new destination address
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 5800a0854ee5..8602cf0d5266 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -52,6 +52,7 @@
#include <sys/sdt.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
+#include <sys/hash.h>
#include <net/if.h>
#include <net/if_types.h>
@@ -175,9 +176,6 @@ VNET_DEFINE(struct in_ifaddrhead, in_ifaddrhead); /* first inet address */
VNET_DEFINE(struct in_ifaddrhashhead *, in_ifaddrhashtbl); /* inet addr hash table */
VNET_DEFINE(u_long, in_ifaddrhmask); /* mask for hash table */
-/* Make sure it is safe to use hashinit(9) on CK_LIST. */
-CTASSERT(sizeof(struct in_ifaddrhashhead) == sizeof(LIST_HEAD(, in_addr)));
-
#ifdef IPCTL_DEFMTU
SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
&ip_mtu, 0, "Default MTU");
@@ -309,24 +307,32 @@ SYSCTL_PROC(_net_inet_ip, IPCTL_INTRDQDROPS, intr_direct_queue_drops,
static void
ip_vnet_init(void *arg __unused)
{
- struct pfil_head_args args;
-
CK_STAILQ_INIT(&V_in_ifaddrhead);
- V_in_ifaddrhashtbl = hashinit(INADDR_NHASH, M_IFADDR, &V_in_ifaddrhmask);
+
+ struct hashalloc_args ha = {
+ .size = INADDR_NHASH,
+ .mtype = M_IFADDR,
+ .mflags = M_WAITOK,
+ .head = HASH_HEAD_CK_LIST,
+ };
+ V_in_ifaddrhashtbl = hashalloc(&ha);
+ V_in_ifaddrhmask = ha.size - 1;
/* Initialize IP reassembly queue. */
ipreass_vnet_init();
/* Initialize packet filter hooks. */
- args.pa_version = PFIL_VERSION;
- args.pa_flags = PFIL_IN | PFIL_OUT;
- args.pa_type = PFIL_TYPE_IP4;
- args.pa_headname = PFIL_INET_NAME;
- V_inet_pfil_head = pfil_head_register(&args);
+ struct pfil_head_args pa = {
+ .pa_version = PFIL_VERSION,
+ .pa_flags = PFIL_IN | PFIL_OUT,
+ .pa_type = PFIL_TYPE_IP4,
+ .pa_headname = PFIL_INET_NAME,
+ };
+ V_inet_pfil_head = pfil_head_register(&pa);
- args.pa_flags = PFIL_OUT;
- args.pa_headname = PFIL_INET_LOCAL_NAME;
- V_inet_local_pfil_head = pfil_head_register(&args);
+ pa.pa_flags = PFIL_OUT;
+ pa.pa_headname = PFIL_INET_LOCAL_NAME;
+ V_inet_local_pfil_head = pfil_head_register(&pa);
if (hhook_head_register(HHOOK_TYPE_IPSEC_IN, AF_INET,
&V_ipsec_hhh_in[HHOOK_IPSEC_INET],
@@ -423,7 +429,12 @@ ip_destroy(void *unused __unused)
ipreass_destroy();
/* Cleanup in_ifaddr hash table; should be empty. */
- hashdestroy(V_in_ifaddrhashtbl, M_IFADDR, V_in_ifaddrhmask);
+ struct hashalloc_args ha = {
+ .mtype = M_IFADDR,
+ .head = HASH_HEAD_CK_LIST,
+ .size = V_in_ifaddrhmask + 1,
+ };
+ hashfree(V_in_ifaddrhashtbl, &ha);
}
VNET_SYSUNINIT(ip, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip_destroy, NULL);
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index 350f5db947af..359755b19e95 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -1385,8 +1385,10 @@ X_ip_mforward(struct ip *ip, struct ifnet *ifp, struct mbuf *m,
* BEGIN: MCAST ROUTING HOT PATH
*/
MRW_RLOCK();
- if (__predict_false(mfct->router == NULL))
+ if (__predict_false(mfct->router == NULL)) {
+ MRW_RUNLOCK();
return (EADDRNOTAVAIL);
+ }
if (imo && ((vifi = imo->imo_multicast_vif) < mfct->numvifs)) {
if (ip->ip_ttl < MAXTTL)
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index a8a4fc1df9e2..6f2b4dd9cb05 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -41,6 +41,7 @@
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/hash.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/protosw.h>
@@ -140,53 +141,12 @@ u_long rip_recvspace = 9216;
SYSCTL_ULONG(_net_inet_raw, OID_AUTO, recvspace, CTLFLAG_RW,
&rip_recvspace, 0, "Maximum space for incoming raw IP datagrams");
-/*
- * Hash functions
- */
-
-#define INP_PCBHASH_RAW_SIZE 256
-#define INP_PCBHASH_RAW(proto, laddr, faddr, mask) \
- (((proto) + (laddr) + (faddr)) % (mask) + 1)
-
-#ifdef INET
-static void
-rip_inshash(struct inpcb *inp)
-{
- struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
- struct inpcbhead *pcbhash;
- int hash;
-
- INP_HASH_WLOCK_ASSERT(pcbinfo);
- INP_WLOCK_ASSERT(inp);
-
- if (inp->inp_ip_p != 0 &&
- inp->inp_laddr.s_addr != INADDR_ANY &&
- inp->inp_faddr.s_addr != INADDR_ANY) {
- hash = INP_PCBHASH_RAW(inp->inp_ip_p, inp->inp_laddr.s_addr,
- inp->inp_faddr.s_addr, pcbinfo->ipi_hashmask);
- } else
- hash = 0;
- pcbhash = &pcbinfo->ipi_hash_exact[hash];
- CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash_exact);
-}
-
-static void
-rip_delhash(struct inpcb *inp)
-{
-
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
- INP_WLOCK_ASSERT(inp);
-
- CK_LIST_REMOVE(inp, inp_hash_exact);
-}
-#endif /* INET */
-
-INPCBSTORAGE_DEFINE(ripcbstor, inpcb, "rawinp", "ripcb", "rip", "riphash");
+INPCBSTORAGE_DEFINE(ripcbstor, inpcb, "rawinp", "ripcb", "riphash");
static void
rip_init(void *arg __unused)
{
-
+#define INP_PCBHASH_RAW_SIZE 256
in_pcbinfo_init(&V_ripcbinfo, &ripcbstor, INP_PCBHASH_RAW_SIZE, 1);
}
VNET_SYSINIT(rip_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, rip_init, NULL);
@@ -250,26 +210,7 @@ struct rip_inp_match_ctx {
};
static bool
-rip_inp_match1(const struct inpcb *inp, void *v)
-{
- struct rip_inp_match_ctx *ctx = v;
-
- if (inp->inp_ip_p != ctx->proto)
- return (false);
-#ifdef INET6
- /* XXX inp locking */
- if ((inp->inp_vflag & INP_IPV4) == 0)
- return (false);
-#endif
- if (inp->inp_laddr.s_addr != ctx->ip->ip_dst.s_addr)
- return (false);
- if (inp->inp_faddr.s_addr != ctx->ip->ip_src.s_addr)
- return (false);
- return (true);
-}
-
-static bool
-rip_inp_match2(const struct inpcb *inp, void *v)
+rip_inp_match(const struct inpcb *inp, void *v)
{
struct rip_inp_match_ctx *ctx = v;
@@ -301,7 +242,7 @@ rip_input(struct mbuf **mp, int *offp, int proto)
.proto = proto,
};
struct inpcb_iterator inpi = INP_ITERATOR(&V_ripcbinfo,
- INPLOOKUP_RLOCKPCB, rip_inp_match1, &ctx);
+ INPLOOKUP_RLOCKPCB, rip_inp_match, &ctx);
struct ifnet *ifp;
struct mbuf *m = *mp;
struct inpcb *inp;
@@ -321,8 +262,7 @@ rip_input(struct mbuf **mp, int *offp, int proto)
fib = M_GETFIB(m);
ifp = m->m_pkthdr.rcvif;
- inpi.hash = INP_PCBHASH_RAW(proto, ctx.ip->ip_src.s_addr,
- ctx.ip->ip_dst.s_addr, V_ripcbinfo.ipi_hashmask);
+ inpi.mode = IN_ADDR_JHASH32(&ctx.ip->ip_src) & V_ripcbinfo.ipi_hashmask;
while ((inp = inp_next(&inpi)) != NULL) {
INP_RLOCK_ASSERT(inp);
if (jailed_without_vnet(inp->inp_cred) &&
@@ -342,8 +282,7 @@ rip_input(struct mbuf **mp, int *offp, int proto)
appended += rip_append(inp, ctx.ip, m, &ripsrc);
}
- inpi.hash = 0;
- inpi.match = rip_inp_match2;
+ inpi.mode = INP_UNCONN_LIST;
MPASS(inpi.inp == NULL);
while ((inp = inp_next(&inpi)) != NULL) {
INP_RLOCK_ASSERT(inp);
@@ -837,9 +776,6 @@ rip_attach(struct socket *so, int proto, struct thread *td)
inp = (struct inpcb *)so->so_pcb;
inp->inp_ip_p = proto;
inp->inp_ip_ttl = V_ip_defttl;
- INP_HASH_WLOCK(&V_ripcbinfo);
- rip_inshash(inp);
- INP_HASH_WUNLOCK(&V_ripcbinfo);
INP_WUNLOCK(inp);
return (0);
}
@@ -859,9 +795,6 @@ rip_detach(struct socket *so)
ip_mrouter_done(so);
INP_WLOCK(inp);
- INP_HASH_WLOCK(&V_ripcbinfo);
- rip_delhash(inp);
- INP_HASH_WUNLOCK(&V_ripcbinfo);
if (ip_rsvp_force_done)
ip_rsvp_force_done(so);
@@ -871,20 +804,17 @@ rip_detach(struct socket *so)
}
static void
-rip_dodisconnect(struct socket *so, struct inpcb *inp)
+rip_dodisconnect(struct inpcb *inp, bool disconnect_socket)
{
- struct inpcbinfo *pcbinfo;
- pcbinfo = inp->inp_pcbinfo;
INP_WLOCK(inp);
- INP_HASH_WLOCK(pcbinfo);
- rip_delhash(inp);
inp->inp_faddr.s_addr = INADDR_ANY;
- rip_inshash(inp);
- INP_HASH_WUNLOCK(pcbinfo);
- SOCK_LOCK(so);
- so->so_state &= ~SS_ISCONNECTED;
- SOCK_UNLOCK(so);
+ ripcb_disconnect(inp);
+ if (disconnect_socket) {
+ SOCK_LOCK(inp->inp_socket);
+ inp->inp_socket->so_state &= ~SS_ISCONNECTED;
+ SOCK_UNLOCK(inp->inp_socket);
+ }
INP_WUNLOCK(inp);
}
@@ -896,7 +826,7 @@ rip_abort(struct socket *so)
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("rip_abort: inp == NULL"));
- rip_dodisconnect(so, inp);
+ rip_dodisconnect(inp, true);
}
static void
@@ -907,7 +837,7 @@ rip_close(struct socket *so)
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("rip_close: inp == NULL"));
- rip_dodisconnect(so, inp);
+ rip_dodisconnect(inp, true);
}
static int
@@ -921,7 +851,7 @@ rip_disconnect(struct socket *so)
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("rip_disconnect: inp == NULL"));
- rip_dodisconnect(so, inp);
+ rip_dodisconnect(inp, true);
return (0);
}
@@ -952,11 +882,7 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
return (EADDRNOTAVAIL);
INP_WLOCK(inp);
- INP_HASH_WLOCK(&V_ripcbinfo);
- rip_delhash(inp);
inp->inp_laddr = addr->sin_addr;
- rip_inshash(inp);
- INP_HASH_WUNLOCK(&V_ripcbinfo);
INP_WUNLOCK(inp);
return (0);
}
@@ -978,11 +904,13 @@ rip_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
KASSERT(inp != NULL, ("rip_connect: inp == NULL"));
INP_WLOCK(inp);
- INP_HASH_WLOCK(&V_ripcbinfo);
- rip_delhash(inp);
- inp->inp_faddr = addr->sin_addr;
- rip_inshash(inp);
- INP_HASH_WUNLOCK(&V_ripcbinfo);
+ if (inp->inp_faddr.s_addr != INADDR_ANY &&
+ addr->sin_addr.s_addr == INADDR_ANY)
+ rip_dodisconnect(inp, false);
+ if (addr->sin_addr.s_addr != INADDR_ANY) {
+ inp->inp_faddr = addr->sin_addr;
+ ripcb_connect(inp);
+ }
soisconnected(so);
INP_WUNLOCK(inp);
return (0);
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 53d9bfd4f445..177b54656ae9 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -898,7 +898,7 @@ struct sctp_association {
uint32_t str_reset_seq_in;
/* various verification tag information */
- uint32_t my_vtag; /* The tag to be used. if assoc is re-initited
+ uint32_t my_vtag; /* The tag to be used. if assoc is re-initiated
* by remote end, and I have unlocked this
* will be regenerated to a new random value. */
uint32_t peer_vtag; /* The peers last tag */
diff --git a/sys/netinet/tcp_hpts.c b/sys/netinet/tcp_hpts.c
index 67b028d7603b..2bb78ed28372 100644
--- a/sys/netinet/tcp_hpts.c
+++ b/sys/netinet/tcp_hpts.c
@@ -510,7 +510,7 @@ tcp_hpts_insert_internal(struct tcpcb *tp, struct tcp_hpts_entry *hpts)
INP_WLOCK_ASSERT(inp);
HPTS_MTX_ASSERT(hpts);
MPASS(hpts->p_cpu == tp->t_hpts_cpu);
- MPASS(!(inp->inp_flags & INP_DROPPED));
+ MPASS(!(tp->t_flags & TF_DISCONNECTED));
hptsh = &hpts->p_hptss[tp->t_hpts_slot];
@@ -615,8 +615,10 @@ __tcp_hpts_remove(struct tcp_hptsi *pace, struct tcpcb *tp)
* tcp_hptsi() moves inpcb to detached tailq
* tcp_hpts_remove() marks as IHPTS_MOVING, slot = -1
* tcp_hpts_insert() sets slot to a meaningful value
- * tcp_hpts_remove() again (we are here!), then in_pcbdrop()
- * tcp_hptsi() finds pcb with meaningful slot and INP_DROPPED
+ * The connection is terminated with the final call to
+ tcp_hpts_remove() again (we are here!) and we fail to call
+ tcp_hpts_release() since it is IHPTS_MOVING. Set slot to -1
+ to delegate the release to the owner of the detached tailq.
*/
tp->t_hpts_slot = -1;
}
@@ -828,7 +830,7 @@ __tcp_hpts_insert(struct tcp_hptsi *pace, struct tcpcb *tp, uint32_t usecs,
bool need_wakeup = false;
INP_WLOCK_ASSERT(tptoinpcb(tp));
- MPASS(!(tptoinpcb(tp)->inp_flags & INP_DROPPED));
+ MPASS(!(tp->t_flags & TF_DISCONNECTED));
MPASS(!(tp->t_in_hpts == IHPTS_ONQUEUE));
/*
@@ -1292,7 +1294,7 @@ again:
}
MPASS(tp->t_in_hpts == IHPTS_ONQUEUE);
- MPASS(!(inp->inp_flags & INP_DROPPED));
+ MPASS(!(tp->t_flags & TF_DISCONNECTED));
KASSERT(runningslot == tp->t_hpts_slot,
("Hpts:%p inp:%p slot mis-aligned %u vs %u",
hpts, inp, runningslot, tp->t_hpts_slot));
@@ -1357,7 +1359,7 @@ again:
*/
__tcp_set_hpts(pace, tp);
}
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
/* Lets do any logging that we might want to */
tcp_hpts_log(hpts, tp, &tv, slots_to_run, i, from_callout);
diff --git a/sys/netinet/tcp_hpts_test.c b/sys/netinet/tcp_hpts_test.c
index 61082adc9063..ea088f5c71cf 100644
--- a/sys/netinet/tcp_hpts_test.c
+++ b/sys/netinet/tcp_hpts_test.c
@@ -175,7 +175,6 @@ dump_tcpcb(struct tcpcb *tp)
/* Input PCB fields that HPTS uses */
KTEST_LOG(ctx, " inp_flags: 0x%x", inp->inp_flags);
- KTEST_LOG(ctx, " INP_DROPPED: %s", (inp->inp_flags & INP_DROPPED) ? "YES" : "NO");
KTEST_LOG(ctx, " inp_flowid: 0x%x", inp->inp_flowid);
KTEST_LOG(ctx, " inp_flowtype: %u", inp->inp_flowtype);
KTEST_LOG(ctx, " inp_numa_domain: %d", inp->inp_numa_domain);
@@ -585,7 +584,7 @@ KTEST_FUNC(tcpcb_initialization)
KTEST_EQUAL(tp->t_lro_cpu, 0);
KTEST_VERIFY(tp->t_hpts_cpu < pace->rp_num_hptss);
KTEST_EQUAL(tp->t_inpcb.inp_refcount, 1);
- KTEST_VERIFY(!(tp->t_inpcb.inp_flags & INP_DROPPED));
+ KTEST_VERIFY(!(tp->t_flags & TF_DISCONNECTED));
test_hpts_free_tcpcb(tp);
tcp_hptsi_stop(pace);
diff --git a/sys/netinet/tcp_log_buf.c b/sys/netinet/tcp_log_buf.c
index 4505171d94d0..3e5955e5db4e 100644
--- a/sys/netinet/tcp_log_buf.c
+++ b/sys/netinet/tcp_log_buf.c
@@ -517,12 +517,12 @@ tcp_log_remove_id_node(struct inpcb *inp, struct tcpcb *tp,
}
#define RECHECK_INP_CLEAN(cleanup) do { \
- if (inp->inp_flags & INP_DROPPED) { \
+ tp = intotcpcb(inp); \
+ if (tp->t_flags & TF_DISCONNECTED) { \
rv = ECONNRESET; \
cleanup; \
goto done; \
} \
- tp = intotcpcb(inp); \
} while (0)
#define RECHECK_INP() RECHECK_INP_CLEAN(/* noop */)
@@ -2254,10 +2254,9 @@ tcp_log_getlogbuf(struct sockopt *sopt, struct tcpcb *tp)
if (error) {
/* Restore list */
+ tp = intotcpcb(inp);
INP_WLOCK(inp);
- if ((inp->inp_flags & INP_DROPPED) == 0) {
- tp = intotcpcb(inp);
-
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
/* Merge the two lists. */
STAILQ_CONCAT(&log_tailq, &tp->t_logs);
tp->t_logs = log_tailq;
@@ -2428,14 +2427,14 @@ tcp_log_dump_tp_logbuf(struct tcpcb *tp, char *reason, int how, bool force)
* may end up dropping some entries. That seems like a
* small price to pay for safety.
*/
- if (inp->inp_flags & INP_DROPPED) {
+ tp = intotcpcb(inp);
+ if (tp->t_flags & TF_DISCONNECTED) {
free(entry, M_TCPLOGDEV);
#ifdef TCPLOG_DEBUG_COUNTERS
counter_u64_add(tcp_log_que_fail2, 1);
#endif
return (ECONNRESET);
}
- tp = intotcpcb(inp);
if (tp->t_lognum == 0) {
free(entry, M_TCPLOGDEV);
return (0);
@@ -2871,14 +2870,14 @@ tcp_log_sendfile(struct socket *so, off_t offset, size_t nbytes, int flags)
/* quick check to see if logging is enabled for this connection */
tp = intotcpcb(inp);
- if ((inp->inp_flags & INP_DROPPED) ||
+ if ((tp->t_flags & TF_DISCONNECTED) ||
(tp->_t_logstate == TCP_LOG_STATE_OFF)) {
return;
}
INP_WLOCK(inp);
/* double check log state now that we have the lock */
- if (inp->inp_flags & INP_DROPPED)
+ if (tp->t_flags & TF_DISCONNECTED)
goto done;
if (tcp_bblogging_on(tp)) {
struct timeval tv;
diff --git a/sys/netinet/tcp_lro.c b/sys/netinet/tcp_lro.c
index 59358db6ecf9..961a1fa2cb31 100644
--- a/sys/netinet/tcp_lro.c
+++ b/sys/netinet/tcp_lro.c
@@ -44,6 +44,7 @@
#include <sys/socketvar.h>
#include <sys/sockbuf.h>
#include <sys/sysctl.h>
+#include <sys/hash.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -190,13 +191,19 @@ tcp_lro_init_args(struct lro_ctrl *lc, struct ifnet *ifp,
LIST_INIT(&lc->lro_free);
LIST_INIT(&lc->lro_active);
- /* create hash table to accelerate entry lookup */
- lc->lro_hash = phashinit_flags(lro_entries, M_LRO, &lc->lro_hashsz,
- HASH_NOWAIT);
+ /* Create hash table to accelerate entry lookup. */
+ struct hashalloc_args ha = {
+ .size = lro_entries,
+ .mtype = M_LRO,
+ .mflags = M_NOWAIT,
+ .type = HASH_TYPE_PRIME,
+ };
+ lc->lro_hash = hashalloc(&ha);
if (lc->lro_hash == NULL) {
memset(lc, 0, sizeof(*lc));
return (ENOMEM);
}
+ lc->lro_hashsz = ha.size;
/* compute size to allocate */
size = (lro_mbufs * sizeof(struct lro_mbuf_sort)) +
@@ -206,7 +213,11 @@ tcp_lro_init_args(struct lro_ctrl *lc, struct ifnet *ifp,
/* check for out of memory */
if (lc->lro_mbuf_data == NULL) {
- free(lc->lro_hash, M_LRO);
+ struct hashalloc_args ha = {
+ .size = lc->lro_hashsz,
+ .mtype = M_LRO,
+ };
+ hashfree(lc->lro_hash, &ha);
memset(lc, 0, sizeof(*lc));
return (ENOMEM);
}
@@ -503,8 +514,11 @@ tcp_lro_free(struct lro_ctrl *lc)
lro_free_mbuf_chain(le->m_head);
}
- /* free hash table */
- free(lc->lro_hash, M_LRO);
+ struct hashalloc_args ha = {
+ .size = lc->lro_hashsz,
+ .mtype = M_LRO,
+ };
+ hashfree(lc->lro_hash, &ha);
lc->lro_hash = NULL;
lc->lro_hashsz = 0;
@@ -1291,27 +1305,6 @@ tcp_lro_rx_common(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum, bool use_h
int error;
uint16_t tcp_data_sum;
-#ifdef INET
- /* Quickly decide if packet cannot be LRO'ed */
- if (__predict_false(V_ipforwarding != 0))
- return (TCP_LRO_CANNOT);
-#endif
-#ifdef INET6
- /* Quickly decide if packet cannot be LRO'ed */
- if (__predict_false(V_ip6_forwarding != 0))
- return (TCP_LRO_CANNOT);
-#endif
- if (((m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) !=
- ((CSUM_DATA_VALID | CSUM_PSEUDO_HDR))) ||
- (m->m_pkthdr.csum_data != 0xffff)) {
- /*
- * The checksum either did not have hardware offload
- * or it was a bad checksum. We can't LRO such
- * a packet.
- */
- counter_u64_add(tcp_bad_csums, 1);
- return (TCP_LRO_CANNOT);
- }
/* We expect a contiguous header [eh, ip, tcp]. */
pa = tcp_lro_parser(m, &po, &pi, true);
if (__predict_false(pa == NULL))
@@ -1429,9 +1422,37 @@ tcp_lro_rx(struct lro_ctrl *lc, struct mbuf *m, uint32_t csum)
{
int error;
+ CURVNET_SET(lc->ifp->if_vnet);
+#ifdef INET
+ /* Quickly decide if packet cannot be LRO'ed */
+ if (__predict_false(V_ipforwarding != 0)) {
+ CURVNET_RESTORE();
+ return (TCP_LRO_CANNOT);
+ }
+#endif
+#ifdef INET6
+ /* Quickly decide if packet cannot be LRO'ed */
+ if (__predict_false(V_ip6_forwarding != 0)) {
+ CURVNET_RESTORE();
+ return (TCP_LRO_CANNOT);
+ }
+#endif
+
+ if (((m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) !=
+ ((CSUM_DATA_VALID | CSUM_PSEUDO_HDR))) ||
+ (m->m_pkthdr.csum_data != 0xffff)) {
+ /*
+ * The checksum either did not have hardware offload
+ * or it was a bad checksum. We can't LRO such
+ * a packet.
+ */
+ counter_u64_add(tcp_bad_csums, 1);
+ CURVNET_RESTORE();
+ return (TCP_LRO_CANNOT);
+ }
+
/* get current time */
binuptime(&lc->lro_last_queue_time);
- CURVNET_SET(lc->ifp->if_vnet);
error = tcp_lro_rx_common(lc, m, csum, true);
if (__predict_false(error != 0)) {
/*
@@ -1458,6 +1479,26 @@ tcp_lro_queue_mbuf(struct lro_ctrl *lc, struct mbuf *mb)
return;
}
+ CURVNET_SET(lc->ifp->if_vnet);
+#ifdef INET
+ /* Quickly decide if packet cannot be LRO'ed */
+ if (__predict_false(V_ipforwarding != 0)) {
+ /* input packet to network layer */
+ CURVNET_RESTORE();
+ (*lc->ifp->if_input) (lc->ifp, mb);
+ return;
+ }
+#endif
+#ifdef INET6
+ /* Quickly decide if packet cannot be LRO'ed */
+ if (__predict_false(V_ip6_forwarding != 0)) {
+ /* input packet to network layer */
+ CURVNET_RESTORE();
+ (*lc->ifp->if_input) (lc->ifp, mb);
+ return;
+ }
+#endif
+ CURVNET_RESTORE();
/* check if packet is not LRO capable */
if (__predict_false((lc->ifp->if_capenable & IFCAP_LRO) == 0)) {
/* input packet to network layer */
@@ -1465,6 +1506,19 @@ tcp_lro_queue_mbuf(struct lro_ctrl *lc, struct mbuf *mb)
return;
}
+ if (((mb->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) !=
+ ((CSUM_DATA_VALID | CSUM_PSEUDO_HDR))) ||
+ (mb->m_pkthdr.csum_data != 0xffff)) {
+ /*
+ * The checksum either did not have hardware offload
+ * or it was a bad checksum. We can't LRO such
+ * a packet.
+ */
+ counter_u64_add(tcp_bad_csums, 1);
+ (*lc->ifp->if_input) (lc->ifp, mb);
+ return;
+ }
+
/* If no hardware or arrival stamp on the packet add timestamp */
if ((tcplro_stacks_wanting_mbufq > 0) &&
(tcp_less_accurate_lro_ts == 0) &&
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 23085f67d5f9..8d88c4f734f6 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -220,6 +220,7 @@ tcp_default_output(struct tcpcb *tp)
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
+ MPASS(!(tp->t_flags & TF_DISCONNECTED));
#ifdef TCP_OFFLOAD
if (tp->t_flags & TF_TOE)
diff --git a/sys/netinet/tcp_stacks/bbr.c b/sys/netinet/tcp_stacks/bbr.c
index 10383bc0801e..6697b6ecc7db 100644
--- a/sys/netinet/tcp_stacks/bbr.c
+++ b/sys/netinet/tcp_stacks/bbr.c
@@ -14217,7 +14217,7 @@ bbr_set_sockopt(struct tcpcb *tp, struct sockopt *sopt)
if (error)
return (error);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
diff --git a/sys/netinet/tcp_stacks/rack.c b/sys/netinet/tcp_stacks/rack.c
index 2e3fcc7a9762..346468fe9a48 100644
--- a/sys/netinet/tcp_stacks/rack.c
+++ b/sys/netinet/tcp_stacks/rack.c
@@ -14750,8 +14750,8 @@ rack_init(struct tcpcb *tp, void **ptr)
*/
rack_convert_rtts(tp);
rack_log_hystart_event(rack, rack->r_ctl.roundends, 20);
- if ((tptoinpcb(tp)->inp_flags & INP_DROPPED) == 0) {
- /* We do not start any timers on DROPPED connections */
+ if ((tp->t_flags & TF_DISCONNECTED) == 0) {
+ /* We do not start any timers on disconnected connections */
if (tp->t_fb->tfb_chg_query == NULL) {
rack_start_hpts_timer(rack, tp, tcp_get_usecs(NULL), 0, 0, 0);
} else {
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index b6bb0221e872..4adc8d859f3e 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -1140,7 +1140,7 @@ static struct mtx isn_mtx;
#define ISN_LOCK() mtx_lock(&isn_mtx)
#define ISN_UNLOCK() mtx_unlock(&isn_mtx)
-INPCBSTORAGE_DEFINE(tcpcbstor, tcpcb, "tcpinp", "tcp_inpcb", "tcp", "tcphash");
+INPCBSTORAGE_DEFINE(tcpcbstor, tcpcb, "tcpinp", "tcp_inpcb", "tcphash");
/*
* Take a value and get the next power of 2 that doesn't overflow.
@@ -2551,10 +2551,20 @@ tcp_close(struct tcpcb *tp)
tcp_timer_stop(tp);
if (tp->t_fb->tfb_tcp_timer_stop_all != NULL)
tp->t_fb->tfb_tcp_timer_stop_all(tp);
- in_pcbdrop(inp);
+#if defined(INET) && defined(INET6)
+ if ((inp->inp_vflag & INP_IPV6) != 0)
+ in6_pcbdisconnect(inp);
+ else
+ in_pcbdisconnect(inp);
+#elif defined(INET6)
+ in6_pcbdisconnect(inp);
+#else
+ in_pcbdisconnect(inp);
+#endif
TCPSTAT_INC(tcps_closed);
if (tp->t_state != TCPS_CLOSED)
tcp_state_change(tp, TCPS_CLOSED);
+ tp->t_flags |= TF_DISCONNECTED;
KASSERT(inp->inp_socket != NULL, ("tcp_close: inp_socket NULL"));
tcp_free_sackholes(tp);
soisdisconnected(so);
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index 1d628bce0d80..22f260db9805 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -846,9 +846,7 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
sin6.sin6_addr = sc->sc_inc.inc6_faddr;
sin6.sin6_port = sc->sc_inc.inc_fport;
sin6.sin6_flowinfo = sin6.sin6_scope_id = 0;
- INP_HASH_WLOCK(&V_tcbinfo);
error = in6_pcbconnect(inp, &sin6, thread0.td_ucred, false);
- INP_HASH_WUNLOCK(&V_tcbinfo);
if (error != 0)
goto abort;
/* Override flowlabel from in6_pcbconnect. */
@@ -875,9 +873,7 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
sin.sin_addr = sc->sc_inc.inc_faddr;
sin.sin_port = sc->sc_inc.inc_fport;
bzero((caddr_t)sin.sin_zero, sizeof(sin.sin_zero));
- INP_HASH_WLOCK(&V_tcbinfo);
error = in_pcbconnect(inp, &sin, thread0.td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
if (error != 0)
goto abort;
}
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c
index b409daf5895e..ca242f2be627 100644
--- a/sys/netinet/tcp_timer.c
+++ b/sys/netinet/tcp_timer.c
@@ -315,7 +315,7 @@ tcp_timer_delack(struct tcpcb *tp)
INP_WLOCK_ASSERT(inp);
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
tp->t_flags |= TF_ACKNOW;
TCPSTAT_INC(tcps_delack);
NET_EPOCH_ENTER(et);
@@ -335,7 +335,7 @@ tcp_timer_2msl(struct tcpcb *tp)
INP_WLOCK_ASSERT(inp);
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO);
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
tcp_log_end_status(tp, TCP_EI_STATUS_2MSL);
tcp_free_sackholes(tp);
/*
@@ -385,7 +385,7 @@ tcp_timer_keep(struct tcpcb *tp)
INP_WLOCK_ASSERT(inp);
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO);
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
/*
* Because we don't regularly reset the keepalive callout in
* the ESTABLISHED state, it may be that we don't actually need
@@ -493,7 +493,7 @@ tcp_timer_persist(struct tcpcb *tp)
INP_WLOCK_ASSERT(inp);
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO);
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
/*
* Persistence timer into zero window.
* Force a byte to be output, if possible.
@@ -560,7 +560,7 @@ tcp_timer_rexmt(struct tcpcb *tp)
INP_WLOCK_ASSERT(inp);
TCP_PROBE2(debug__user, tp, PRU_SLOWTIMO);
- CURVNET_SET(inp->inp_vnet);
+ CURVNET_SET(inp->inp_socket->so_vnet);
if (tp->t_fb->tfb_tcp_rexmit_tmr) {
/* The stack has a timer action too. */
(*tp->t_fb->tfb_tcp_rexmit_tmr)(tp);
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
index ba5c90c91e43..eaa2fa336a94 100644
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -126,10 +126,7 @@ tcp_twstart(struct tcpcb *tp)
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
-
- /* A dropped inp should never transition to TIME_WAIT state. */
- KASSERT((inp->inp_flags & INP_DROPPED) == 0, ("tcp_twstart: "
- "(inp->inp_flags & INP_DROPPED) != 0"));
+ MPASS(!(tp->t_flags & TF_DISCONNECTED));
tcp_state_change(tp, TCPS_TIME_WAIT);
tcp_free_sackholes(tp);
@@ -218,12 +215,17 @@ tcp_twcheck(struct inpcb *inp, struct tcpopt *to, struct tcphdr *th,
/*
* If a new connection request is received
* while in TIME_WAIT, drop the old connection
- * and start over if the sequence numbers
- * are above the previous ones.
+ * and start over if allowed by RFC 6191.
* Allow UDP port number changes in this case.
*/
if (((thflags & (TH_SYN | TH_ACK)) == TH_SYN) &&
- SEQ_GT(th->th_seq, tp->rcv_nxt)) {
+ ((((tp->t_flags & TF_RCVD_TSTMP) != 0) &&
+ ((to->to_flags & TOF_TS) != 0) &&
+ TSTMP_LT(tp->ts_recent, to->to_tsval)) ||
+ (((tp->t_flags & TF_RCVD_TSTMP) == 0) &&
+ ((to->to_flags & TOF_TS) != 0) &&
+ (V_tcp_tolerate_missing_ts == 0)) ||
+ SEQ_GT(th->th_seq, tp->rcv_nxt))) {
/*
* In case we can't upgrade our lock just pretend we have
* lost this packet.
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index b0a75127b124..6a51068d0461 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -205,9 +205,9 @@ tcp_usr_detach(struct socket *so)
tp = intotcpcb(inp);
- KASSERT(inp->inp_flags & INP_DROPPED ||
+ KASSERT(tp->t_flags & TF_DISCONNECTED ||
tp->t_state < TCPS_SYN_SENT,
- ("%s: inp %p not dropped or embryonic", __func__, inp));
+ ("%s: inp %p not disconnected or embryonic", __func__, inp));
tcp_discardcb(tp);
in_pcbfree(inp);
@@ -220,19 +220,16 @@ tcp_usr_detach(struct socket *so)
static int
tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
{
- int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct sockaddr_in *sinp;
+ int error = 0;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_bind: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (EINVAL);
}
- tp = intotcpcb(inp);
sinp = (struct sockaddr_in *)nam;
if (nam->sa_family != AF_INET) {
@@ -259,10 +256,8 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
error = EAFNOSUPPORT;
goto out;
}
- INP_HASH_WLOCK(&V_tcbinfo);
error = in_pcbbind(inp, sinp, V_tcp_bind_all_fibs ? 0 : INPBIND_FIB,
td->td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
out:
tcp_bblog_pru(tp, PRU_BIND, error);
TCP_PROBE2(debug__user, tp, PRU_BIND);
@@ -276,20 +271,17 @@ out:
static int
tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
{
- int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct sockaddr_in6 *sin6;
+ int error = 0;
u_char vflagsav;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (EINVAL);
}
- tp = intotcpcb(inp);
vflagsav = inp->inp_vflag;
@@ -311,7 +303,6 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
goto out;
}
- INP_HASH_WLOCK(&V_tcbinfo);
inp->inp_vflag &= ~INP_IPV4;
inp->inp_vflag |= INP_IPV6;
#ifdef INET
@@ -324,20 +315,17 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
in6_sin6_2_sin(&sin, sin6);
if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) {
error = EAFNOSUPPORT;
- INP_HASH_WUNLOCK(&V_tcbinfo);
goto out;
}
inp->inp_vflag |= INP_IPV4;
inp->inp_vflag &= ~INP_IPV6;
error = in_pcbbind(inp, &sin, 0, td->td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
goto out;
}
}
#endif
error = in6_pcbbind(inp, sin6, V_tcp_bind_all_fibs ? 0 : INPBIND_FIB,
td->td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
out:
if (error != 0)
inp->inp_vflag = vflagsav;
@@ -355,19 +343,16 @@ out:
static int
tcp_usr_listen(struct socket *so, int backlog, struct thread *td)
{
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error = 0;
bool already_listening;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_listen: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (EINVAL);
}
- tp = intotcpcb(inp);
SOCK_LOCK(so);
already_listening = SOLISTENING(so);
@@ -377,10 +362,8 @@ tcp_usr_listen(struct socket *so, int backlog, struct thread *td)
goto out;
}
if (inp->inp_lport == 0) {
- INP_HASH_WLOCK(&V_tcbinfo);
error = in_pcbbind(inp, NULL,
V_tcp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
}
if (error == 0) {
tcp_state_change(tp, TCPS_LISTEN);
@@ -414,20 +397,17 @@ out:
static int
tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
{
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
u_char vflagsav;
int error = 0;
bool already_listening;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (EINVAL);
}
- tp = intotcpcb(inp);
vflagsav = inp->inp_vflag;
@@ -438,7 +418,6 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
SOCK_UNLOCK(so);
goto out;
}
- INP_HASH_WLOCK(&V_tcbinfo);
if (inp->inp_lport == 0) {
inp->inp_vflag &= ~INP_IPV4;
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
@@ -446,7 +425,6 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
error = in6_pcbbind(inp, NULL,
V_tcp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
}
- INP_HASH_WUNLOCK(&V_tcbinfo);
if (error == 0) {
tcp_state_change(tp, TCPS_LISTEN);
solisten_proto(so, backlog);
@@ -488,19 +466,16 @@ static int
tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct epoch_tracker et;
- int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct sockaddr_in *sinp;
+ int error = 0;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_connect: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (ECONNREFUSED);
}
- tp = intotcpcb(inp);
sinp = (struct sockaddr_in *)nam;
if (nam->sa_family != AF_INET) {
@@ -556,21 +531,18 @@ static int
tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct epoch_tracker et;
- int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
struct sockaddr_in6 *sin6;
+ int error = 0;
u_int8_t incflagsav;
u_char vflagsav;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (ECONNREFUSED);
}
- tp = intotcpcb(inp);
vflagsav = inp->inp_vflag;
incflagsav = inp->inp_inc.inc_flags;
@@ -725,18 +697,15 @@ out:
static int
tcp_usr_accept(struct socket *so, struct sockaddr *sa)
{
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error = 0;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_accept: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (ECONNABORTED);
}
- tp = intotcpcb(inp);
if (so->so_state & SS_ISDISCONNECTED)
error = ECONNABORTED;
@@ -759,18 +728,15 @@ tcp_usr_accept(struct socket *so, struct sockaddr *sa)
static int
tcp6_usr_accept(struct socket *so, struct sockaddr *sa)
{
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error = 0;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp6_usr_accept: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (ECONNABORTED);
}
- tp = intotcpcb(inp);
if (so->so_state & SS_ISDISCONNECTED) {
error = ECONNABORTED;
@@ -842,7 +808,7 @@ tcp_usr_shutdown(struct socket *so, enum shutdown_how how)
* return ECONNRESEST for SHUT_RD as well?
*/
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
@@ -868,18 +834,16 @@ static int
tcp_usr_rcvd(struct socket *so, int flags)
{
struct epoch_tracker et;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int outrv = 0, error = 0;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_rcvd: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
+ /* XXXGL: how could this happen?! */
INP_WUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
NET_EPOCH_ENTER(et);
/*
@@ -917,9 +881,8 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *nam, struct mbuf *control, struct thread *td)
{
struct epoch_tracker et;
- int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
#ifdef INET
#ifdef INET6
struct sockaddr_in sin;
@@ -930,20 +893,18 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr_in6 *sin6;
int isipv6;
#endif
+ int error = 0;
u_int8_t incflagsav;
u_char vflagsav;
bool restoreflags;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
if (m != NULL && (flags & PRUS_NOTREADY) == 0)
m_freem(m);
INP_WUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
vflagsav = inp->inp_vflag;
incflagsav = inp->inp_inc.inc_flags;
@@ -1121,8 +1082,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
if (tp->t_fbyte_out && tp->t_fbyte_in)
tp->t_flags2 |= TF2_FBYTES_COMPLETE;
}
- if (!(inp->inp_flags & INP_DROPPED) &&
- !(flags & PRUS_NOTREADY)) {
+ if (!(flags & PRUS_NOTREADY)) {
if (flags & PRUS_MORETOCOME)
tp->t_flags |= TF_MORETOCOME;
error = tcp_output_nodrop(tp);
@@ -1232,18 +1192,16 @@ static int
tcp_usr_ready(struct socket *so, struct mbuf *m, int count)
{
struct epoch_tracker et;
- struct inpcb *inp;
- struct tcpcb *tp;
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error;
- inp = sotoinpcb(so);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
mb_free_notready(m, count);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
SOCK_SENDBUF_LOCK(so);
error = sbready(&so->so_snd, m, count);
@@ -1265,30 +1223,23 @@ tcp_usr_ready(struct socket *so, struct mbuf *m, int count)
static void
tcp_usr_abort(struct socket *so)
{
- struct inpcb *inp;
- struct tcpcb *tp;
struct epoch_tracker et;
-
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL"));
-
- NET_EPOCH_ENTER(et);
- INP_WLOCK(inp);
- KASSERT(inp->inp_socket != NULL,
- ("tcp_usr_abort: inp_socket == NULL"));
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
/*
* If we still have full TCP state, and we're not dropped, drop.
*/
- if (!(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
+ NET_EPOCH_ENTER(et);
+ INP_WLOCK(inp);
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
tp = tcp_drop(tp, ECONNABORTED);
if (tp == NULL)
goto dropped;
tcp_bblog_pru(tp, PRU_ABORT, 0);
TCP_PROBE2(debug__user, tp, PRU_ABORT);
}
- if (!(inp->inp_flags & INP_DROPPED)) {
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
soref(so);
inp->inp_flags |= INP_SOCKREF;
}
@@ -1303,24 +1254,17 @@ dropped:
static void
tcp_usr_close(struct socket *so)
{
- struct inpcb *inp;
- struct tcpcb *tp;
struct epoch_tracker et;
-
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL"));
-
- NET_EPOCH_ENTER(et);
- INP_WLOCK(inp);
- KASSERT(inp->inp_socket != NULL,
- ("tcp_usr_close: inp_socket == NULL"));
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
/*
* If we are still connected and we're not dropped, initiate
* a disconnect.
*/
- if (!(inp->inp_flags & INP_DROPPED)) {
- tp = intotcpcb(inp);
+ NET_EPOCH_ENTER(et);
+ INP_WLOCK(inp);
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
if (tp->t_state != TCPS_TIME_WAIT) {
tp->t_flags |= TF_CLOSED;
tcp_disconnect(tp);
@@ -1328,7 +1272,7 @@ tcp_usr_close(struct socket *so)
TCP_PROBE2(debug__user, tp, PRU_CLOSE);
}
}
- if (!(inp->inp_flags & INP_DROPPED)) {
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
soref(so);
inp->inp_flags |= INP_SOCKREF;
}
@@ -1360,18 +1304,16 @@ tcp_pru_options_support(struct tcpcb *tp, int flags)
static int
tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags)
{
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
int error = 0;
- struct inpcb *inp;
- struct tcpcb *tp;
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_usr_rcvoob: inp == NULL"));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
+ /* XXXGL: how could this happen?! */
INP_WUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
error = tcp_pru_options_support(tp, PRUS_OOB);
if (error) {
@@ -1482,9 +1424,7 @@ tcp_connect(struct tcpcb *tp, struct sockaddr_in *sin, struct thread *td)
if (__predict_false((so->so_options & SO_REUSEPORT_LB) != 0))
return (EOPNOTSUPP);
- INP_HASH_WLOCK(&V_tcbinfo);
error = in_pcbconnect(inp, sin, td->td_ucred);
- INP_HASH_WUNLOCK(&V_tcbinfo);
if (error != 0)
return (error);
@@ -1531,9 +1471,7 @@ tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *sin6, struct thread *td)
if (__predict_false((so->so_options & SO_REUSEPORT_LB) != 0))
return (EOPNOTSUPP);
- INP_HASH_WLOCK(&V_tcbinfo);
error = in6_pcbconnect(inp, sin6, td->td_ucred, true);
- INP_HASH_WUNLOCK(&V_tcbinfo);
if (error != 0)
return (error);
@@ -1650,15 +1588,16 @@ tcp_fill_info(const struct tcpcb *tp, struct tcp_info *ti)
* socket option arguments. When it re-acquires the lock after the copy, it
* has to revalidate that the connection is still valid for the socket
* option.
+ * XXXGL: review if this is really needed
*/
#define INP_WLOCK_RECHECK_CLEANUP(inp, cleanup) do { \
INP_WLOCK(inp); \
- if (inp->inp_flags & INP_DROPPED) { \
+ tp = intotcpcb(inp); \
+ if (tp->t_flags & TF_DISCONNECTED) { \
INP_WUNLOCK(inp); \
cleanup; \
return (ECONNRESET); \
} \
- tp = intotcpcb(inp); \
} while(0)
#define INP_WLOCK_RECHECK(inp) INP_WLOCK_RECHECK_CLEANUP((inp), /* noop */)
@@ -1671,8 +1610,8 @@ tcp_ctloutput_set(struct inpcb *inp, struct sockopt *sopt)
MPASS(sopt->sopt_dir == SOPT_SET);
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("inp_flags == %x", inp->inp_flags));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("tp_flags == %x", tp->t_flags));
KASSERT(so != NULL, ("inp_socket == NULL"));
if (sopt->sopt_level != IPPROTO_TCP) {
@@ -1839,8 +1778,8 @@ tcp_ctloutput_get(struct inpcb *inp, struct sockopt *sopt)
MPASS(sopt->sopt_dir == SOPT_GET);
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("inp_flags == %x", inp->inp_flags));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("tp_flags == %x", tp->t_flags));
KASSERT(so != NULL, ("inp_socket == NULL"));
if (sopt->sopt_level != IPPROTO_TCP) {
@@ -1883,13 +1822,11 @@ tcp_ctloutput_get(struct inpcb *inp, struct sockopt *sopt)
int
tcp_ctloutput(struct socket *so, struct sockopt *sopt)
{
- struct inpcb *inp;
-
- inp = sotoinpcb(so);
- KASSERT(inp != NULL, ("tcp_ctloutput: inp == NULL"));
+ struct inpcb *inp = sotoinpcb(so);
+ struct tcpcb *tp = intotcpcb(inp);
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
@@ -1917,7 +1854,7 @@ tcp_set_cc_mod(struct inpcb *inp, struct sockopt *sopt)
{
struct cc_algo *algo;
void *ptr = NULL;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
struct cc_var cc_mem;
char buf[TCP_CA_NAME_MAX];
size_t mem_sz;
@@ -1967,7 +1904,7 @@ no_mem_needed:
*/
memset(&cc_mem, 0, sizeof(cc_mem));
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (__predict_false(tp->t_flags & TF_DISCONNECTED)) {
INP_WUNLOCK(inp);
if (ptr)
free(ptr, M_CC_MEM);
@@ -1977,7 +1914,6 @@ no_mem_needed:
CC_LIST_RUNLOCK();
return (ECONNRESET);
}
- tp = intotcpcb(inp);
if (ptr != NULL)
memset(ptr, 0, mem_sz);
cc_mem.tp = tp;
@@ -2043,8 +1979,8 @@ tcp_default_ctloutput(struct tcpcb *tp, struct sockopt *sopt)
size_t len;
INP_WLOCK_ASSERT(inp);
- KASSERT((inp->inp_flags & INP_DROPPED) == 0,
- ("inp_flags == %x", inp->inp_flags));
+ KASSERT((tp->t_flags & TF_DISCONNECTED) == 0,
+ ("tp_flags == %x", tp->t_flags));
KASSERT(inp->inp_socket != NULL, ("inp_socket == NULL"));
switch (sopt->sopt_level) {
@@ -2650,11 +2586,10 @@ unhold:
static void
tcp_disconnect(struct tcpcb *tp)
{
- struct inpcb *inp = tptoinpcb(tp);
struct socket *so = tptosocket(tp);
NET_EPOCH_ASSERT();
- INP_WLOCK_ASSERT(inp);
+ INP_WLOCK_ASSERT(tptoinpcb(tp));
/*
* Neither tcp_close() nor tcp_drop() should return NULL, as the
@@ -2673,7 +2608,7 @@ tcp_disconnect(struct tcpcb *tp)
soisdisconnecting(so);
sbflush(&so->so_rcv);
tcp_usrclosed(tp);
- if (!(inp->inp_flags & INP_DROPPED))
+ if (!(tp->t_flags & TF_DISCONNECTED))
/* Ignore stack's drop request, we already at it. */
(void)tcp_output_nodrop(tp);
}
@@ -2841,9 +2776,12 @@ db_print_bblog_state(int state)
static void
db_print_tcpcb(struct tcpcb *tp, const char *name, int indent, bool show_bblog,
- bool show_inpcb)
+ bool show_inpcb, bool only_locked)
{
+ if (only_locked && tp->t_inpcb.inp_lock.rw_lock == RW_UNLOCKED)
+ return;
+
db_print_indent(indent);
db_printf("%s at %p\n", name, tp);
@@ -2987,14 +2925,13 @@ DB_SHOW_COMMAND(tcpcb, db_show_tcpcb)
show_bblog = strchr(modif, 'b') != NULL;
show_inpcb = strchr(modif, 'i') != NULL;
tp = (struct tcpcb *)addr;
- db_print_tcpcb(tp, "tcpcb", 0, show_bblog, show_inpcb);
+ db_print_tcpcb(tp, "tcpcb", 0, show_bblog, show_inpcb, false);
}
DB_SHOW_ALL_COMMAND(tcpcbs, db_show_all_tcpcbs)
{
VNET_ITERATOR_DECL(vnet_iter);
struct inpcb *inp;
- struct tcpcb *tp;
bool only_locked, show_bblog, show_inpcb;
only_locked = strchr(modif, 'l') != NULL;
@@ -3002,18 +2939,23 @@ DB_SHOW_ALL_COMMAND(tcpcbs, db_show_all_tcpcbs)
show_inpcb = strchr(modif, 'i') != NULL;
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
- CK_LIST_FOREACH(inp, &V_tcbinfo.ipi_listhead, inp_list) {
- if (only_locked &&
- inp->inp_lock.rw_lock == RW_UNLOCKED)
- continue;
- tp = intotcpcb(inp);
- db_print_tcpcb(tp, "tcpcb", 0, show_bblog, show_inpcb);
+ for (u_int i = 0; i <= V_tcbinfo.ipi_porthashmask; i++)
+ CK_LIST_FOREACH(inp, &V_tcbinfo.ipi_porthashbase[i],
+ inp_portlist) {
+ db_print_tcpcb(intotcpcb(inp), "tcpcb", 0,
+ show_bblog, show_inpcb, only_locked);
+ if (db_pager_quit)
+ goto break_hash;
+ }
+break_hash:
+ CK_LIST_FOREACH(inp, &V_tcbinfo.ipi_list_unconn,
+ inp_unconn_list) {
+ db_print_tcpcb(intotcpcb(inp), "tcpcb", 0,
+ show_bblog, show_inpcb, only_locked);
if (db_pager_quit)
break;
}
CURVNET_RESTORE();
- if (db_pager_quit)
- break;
}
}
#endif
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 95c4e4c52ba0..987bb98c19af 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -789,7 +789,7 @@ tcp_packets_this_ack(struct tcpcb *tp, tcp_seq ack)
#define TF_TSO 0x01000000 /* TSO enabled on this connection */
#define TF_TOE 0x02000000 /* this connection is offloaded */
#define TF_CLOSED 0x04000000 /* close(2) called on socket */
-#define TF_UNUSED 0x08000000 /* was TF_SENTSYN */
+#define TF_DISCONNECTED 0x08000000 /* went through tcp_close() */
#define TF_LRD 0x10000000 /* Lost Retransmission Detection */
#define TF_CONGRECOVERY 0x20000000 /* congestion recovery mode */
#define TF_WASCRECOVERY 0x40000000 /* was in congestion recovery */
diff --git a/sys/netinet/toecore.c b/sys/netinet/toecore.c
index 1c4e89069a4b..38171f3439be 100644
--- a/sys/netinet/toecore.c
+++ b/sys/netinet/toecore.c
@@ -212,16 +212,15 @@ static void
toe_listen_start(struct inpcb *inp, void *arg)
{
struct toedev *t, *tod;
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
INP_WLOCK_ASSERT(inp);
KASSERT(inp->inp_pcbinfo == &V_tcbinfo,
("%s: inp is not a TCP inp", __func__));
- if (inp->inp_flags & INP_DROPPED)
+ if (tp->t_flags & TF_DISCONNECTED)
return;
- tp = intotcpcb(inp);
if (tp->t_state != TCPS_LISTEN)
return;
@@ -510,13 +509,12 @@ toe_l2_resolve(struct toedev *tod, struct ifnet *ifp, struct sockaddr *sa,
void
toe_connect_failed(struct toedev *tod, struct inpcb *inp, int err)
{
+ struct tcpcb *tp = intotcpcb(inp);
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
- if (!(inp->inp_flags & INP_DROPPED)) {
- struct tcpcb *tp = intotcpcb(inp);
-
+ if (!(tp->t_flags & TF_DISCONNECTED)) {
KASSERT(tp->t_flags & TF_TOE,
("%s: tp %p not offloaded.", __func__, tp));
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index e8847ea12f1e..23b0ca684b09 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -165,9 +165,9 @@ VNET_PCPUSTAT_SYSUNINIT(udpstat);
static void udp_detach(struct socket *so);
#endif
-INPCBSTORAGE_DEFINE(udpcbstor, udpcb, "udpinp", "udp_inpcb", "udp", "udphash");
+INPCBSTORAGE_DEFINE(udpcbstor, udpcb, "udpinp", "udp_inpcb", "udphash");
INPCBSTORAGE_DEFINE(udplitecbstor, udpcb, "udpliteinp", "udplite_inpcb",
- "udplite", "udplitehash");
+ "udplitehash");
static void
udp_vnet_init(void *arg __unused)
@@ -1117,7 +1117,6 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
int len, error = 0;
struct in_addr faddr, laddr;
struct cmsghdr *cm;
- struct inpcbinfo *pcbinfo;
struct sockaddr_in *sin, src;
struct epoch_tracker et;
int cscov_partial = 0;
@@ -1289,7 +1288,6 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
goto release;
pr = inp->inp_socket->so_proto->pr_protocol;
- pcbinfo = udp_get_inpcbinfo(pr);
/*
* If the IP_SENDSRCADDR control message was specified, override the
@@ -1310,10 +1308,8 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
inp->inp_vflag |= INP_IPV4;
inp->inp_vflag &= ~INP_IPV6;
}
- INP_HASH_WLOCK(pcbinfo);
error = in_pcbbind_setup(inp, &src, &laddr.s_addr, &lport,
V_udp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
if ((flags & PRUS_IPV6) != 0)
inp->inp_vflag = vflagsav;
if (error)
@@ -1356,10 +1352,8 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
.sin_len = sizeof(struct sockaddr_in),
};
- INP_HASH_WLOCK(pcbinfo);
error = in_pcbbind(inp, &wild, V_udp_bind_all_fibs ?
0 : INPBIND_FIB, td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
if (error)
goto release;
lport = inp->inp_lport;
@@ -1528,16 +1522,13 @@ void
udp_abort(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_abort: inp == NULL"));
INP_WLOCK(inp);
if (inp->inp_faddr.s_addr != INADDR_ANY) {
- INP_HASH_WLOCK(pcbinfo);
in_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ inp->inp_laddr.s_addr = INADDR_ANY;
soisdisconnected(so);
}
INP_WUNLOCK(inp);
@@ -1603,11 +1594,9 @@ static int
udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
struct sockaddr_in *sinp;
int error;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_bind: inp == NULL"));
@@ -1626,10 +1615,8 @@ udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
return (EINVAL);
INP_WLOCK(inp);
- INP_HASH_WLOCK(pcbinfo);
error = in_pcbbind(inp, sinp, V_udp_bind_all_fibs ? 0 : INPBIND_FIB,
td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
INP_WUNLOCK(inp);
return (error);
}
@@ -1638,16 +1625,13 @@ static void
udp_close(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_close: inp == NULL"));
INP_WLOCK(inp);
if (inp->inp_faddr.s_addr != INADDR_ANY) {
- INP_HASH_WLOCK(pcbinfo);
in_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ inp->inp_laddr.s_addr = INADDR_ANY;
soisdisconnected(so);
}
INP_WUNLOCK(inp);
@@ -1658,11 +1642,9 @@ udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct epoch_tracker et;
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
struct sockaddr_in *sin;
int error;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_connect: inp == NULL"));
@@ -1683,9 +1665,7 @@ udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
return (error);
}
NET_EPOCH_ENTER(et);
- INP_HASH_WLOCK(pcbinfo);
error = in_pcbconnect(inp, sin, td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
NET_EPOCH_EXIT(et);
if (error == 0)
soisconnected(so);
@@ -1710,9 +1690,7 @@ int
udp_disconnect(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_disconnect: inp == NULL"));
INP_WLOCK(inp);
@@ -1720,9 +1698,8 @@ udp_disconnect(struct socket *so)
INP_WUNLOCK(inp);
return (ENOTCONN);
}
- INP_HASH_WLOCK(pcbinfo);
in_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ inp->inp_laddr.s_addr = INADDR_ANY;
SOCK_LOCK(so);
so->so_state &= ~SS_ISCONNECTED; /* XXX */
SOCK_UNLOCK(so);
diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c
index 5159faa1e2e8..18e13175f739 100644
--- a/sys/netinet6/in6_gif.c
+++ b/sys/netinet6/in6_gif.c
@@ -47,6 +47,7 @@
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/proc.h>
+#include <sys/hash.h>
#include <net/ethernet.h>
#include <net/if.h>
@@ -86,15 +87,17 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim,
*/
VNET_DEFINE_STATIC(struct gif_list *, ipv6_hashtbl) = NULL;
VNET_DEFINE_STATIC(struct gif_list *, ipv6_srchashtbl) = NULL;
+VNET_DEFINE_STATIC(u_int, ipv6_hashmask);
VNET_DEFINE_STATIC(struct gif_list, ipv6_list) = CK_LIST_HEAD_INITIALIZER();
#define V_ipv6_hashtbl VNET(ipv6_hashtbl)
#define V_ipv6_srchashtbl VNET(ipv6_srchashtbl)
+#define V_ipv6_hashmask VNET(ipv6_hashmask)
#define V_ipv6_list VNET(ipv6_list)
#define GIF_HASH(src, dst) (V_ipv6_hashtbl[\
- in6_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+ in6_gif_hashval((src), (dst)) & (V_ipv6_hashmask - 1)])
#define GIF_SRCHASH(src) (V_ipv6_srchashtbl[\
- fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
+ fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (V_ipv6_hashmask - 1)])
#define GIF_HASH_SC(sc) GIF_HASH(&(sc)->gif_ip6hdr->ip6_src,\
&(sc)->gif_ip6hdr->ip6_dst)
static uint32_t
@@ -237,8 +240,15 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
break;
if (V_ipv6_hashtbl == NULL) {
- V_ipv6_hashtbl = gif_hashinit();
- V_ipv6_srchashtbl = gif_hashinit();
+ struct hashalloc_args ha = {
+ .size = GIF_HASH_SIZE,
+ .mtype = M_GIF,
+ .mflags = M_WAITOK,
+ .head = HASH_HEAD_CK_LIST,
+ };
+ V_ipv6_hashtbl = hashalloc(&ha);
+ V_ipv6_srchashtbl = hashalloc(&ha);
+ V_ipv6_hashmask = ha.size - 1;
}
error = in6_gif_checkdup(sc, &src->sin6_addr,
&dst->sin6_addr);
@@ -469,9 +479,14 @@ in6_gif_uninit(void)
ip6_encap_unregister_srcaddr(ipv6_srcaddrtab);
}
if (V_ipv6_hashtbl != NULL) {
- gif_hashdestroy(V_ipv6_hashtbl);
+ struct hashalloc_args ha = {
+ .size = V_ipv6_hashmask + 1,
+ .mtype = M_GIF,
+ .head = HASH_HEAD_CK_LIST,
+ };
+ hashfree(V_ipv6_hashtbl, &ha);
V_ipv6_hashtbl = NULL;
GIF_WAIT();
- gif_hashdestroy(V_ipv6_srchashtbl);
+ hashfree(V_ipv6_srchashtbl, &ha);
}
}
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 216051156767..3750c7040fbc 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -112,18 +112,16 @@
#include <netinet6/in6_fib.h>
#include <netinet6/scope6_var.h>
-int
-in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
+static int
+in6_pcbsetport_locked(struct in6_addr *laddr, struct inpcb *inp,
+ struct ucred *cred)
{
struct socket *so = inp->inp_socket;
u_int16_t lport = 0;
int error, lookupflags = 0;
-#ifdef INVARIANTS
- struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
-#endif
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(pcbinfo);
+ INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
error = prison_local_ip6(cred, laddr,
((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0));
@@ -150,6 +148,18 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
return (0);
}
+int
+in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred)
+{
+ int error;
+
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
+ error = in6_pcbsetport_locked(laddr, inp, cred);
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+
+ return (error);
+}
+
/*
* Determine whether the inpcb can be bound to the specified address/port tuple.
*/
@@ -297,7 +307,6 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
int error, fib, lookupflags, sooptions;
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
return (EINVAL);
@@ -310,6 +319,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
if ((error = prison_local_ip6(cred, &inp->in6p_laddr,
((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0)
return (error);
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
} else {
KASSERT(sin6->sin6_family == AF_INET6,
("%s: invalid address family for %p", __func__, sin6));
@@ -326,11 +336,14 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
fib = (flags & INPBIND_FIB) != 0 ? inp->inp_inc.inc_fibnum :
RT_ALL_FIBS;
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
/* See if this address/port combo is available. */
- error = in6_pcbbind_avail(inp, sin6, fib, sooptions, lookupflags,
- cred);
- if (error != 0)
+ error = in6_pcbbind_avail(inp, sin6, fib, sooptions,
+ lookupflags, cred);
+ if (error != 0) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
return (error);
+ }
lport = sin6->sin6_port;
inp->in6p_laddr = sin6->sin6_addr;
@@ -338,7 +351,9 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
if ((flags & INPBIND_FIB) != 0)
inp->inp_flags |= INP_BOUNDFIB;
if (lport == 0) {
- if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) {
+ error = in6_pcbsetport_locked(&inp->in6p_laddr, inp, cred);
+ if (__predict_false(error != 0)) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
/* Undo an address bind that may have occurred. */
inp->inp_flags &= ~INP_BOUNDFIB;
inp->in6p_laddr = in6addr_any;
@@ -347,12 +362,15 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
} else {
inp->inp_lport = lport;
if (in_pcbinshash(inp) != 0) {
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
inp->inp_flags &= ~INP_BOUNDFIB;
inp->in6p_laddr = in6addr_any;
inp->inp_lport = 0;
return (EAGAIN);
}
}
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+
return (0);
}
@@ -437,7 +455,6 @@ in6_pcbconnect(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred,
NET_EPOCH_ASSERT();
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(pcbinfo);
KASSERT(sin6->sin6_family == AF_INET6,
("%s: invalid address family for %p", __func__, sin6));
KASSERT(sin6->sin6_len == sizeof(*sin6),
@@ -461,23 +478,30 @@ in6_pcbconnect(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred,
* Call inner routine, to assign local interface address.
* in6_pcbladdr() may automatically fill in sin6_scope_id.
*/
+ INP_HASH_WLOCK(pcbinfo);
if ((error = in6_pcbladdr(inp, sin6, &laddr6.sin6_addr,
- sas_required)) != 0)
+ sas_required)) != 0) {
+ INP_HASH_WUNLOCK(pcbinfo);
return (error);
+ }
if (in6_pcblookup_hash_locked(pcbinfo, &sin6->sin6_addr,
sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ?
&laddr6.sin6_addr : &inp->in6p_laddr, inp->inp_lport, 0,
- M_NODOM, RT_ALL_FIBS) != NULL)
+ M_NODOM, RT_ALL_FIBS) != NULL) {
+ INP_HASH_WUNLOCK(pcbinfo);
return (EADDRINUSE);
+ }
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
if (inp->inp_lport == 0) {
error = in_pcb_lport_dest(inp,
(struct sockaddr *) &laddr6, &inp->inp_lport,
(struct sockaddr *) sin6, sin6->sin6_port, cred,
INPLOOKUP_WILDCARD);
- if (error)
+ if (__predict_false(error)) {
+ INP_HASH_WUNLOCK(pcbinfo);
return (error);
+ }
}
inp->in6p_laddr = laddr6.sin6_addr;
}
@@ -489,12 +513,12 @@ in6_pcbconnect(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred,
inp->inp_flow |=
(htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK);
- if ((inp->inp_flags & INP_INHASHLIST) != 0) {
- in_pcbrehash(inp);
- } else {
+ if (inp->inp_flags & INP_UNCONNECTED) {
error = in_pcbinshash(inp);
MPASS(error == 0);
- }
+ } else
+ in_pcbrehash(inp);
+ INP_HASH_WUNLOCK(pcbinfo);
return (0);
}
@@ -504,19 +528,26 @@ in6_pcbdisconnect(struct inpcb *inp)
{
INP_WLOCK_ASSERT(inp);
- INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
KASSERT(inp->inp_smr == SMR_SEQ_INVALID,
("%s: inp %p was already disconnected", __func__, inp));
- in_pcbremhash_locked(inp);
+ if (inp->inp_flags & INP_UNCONNECTED)
+ return;
- /* See the comment in in_pcbinshash(). */
- inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
+ INP_HASH_WLOCK(inp->inp_pcbinfo);
+ in_pcbremhash(inp);
+ inp->inp_flags |= INP_UNCONNECTED;
+ CK_LIST_INSERT_HEAD(&inp->inp_pcbinfo->ipi_list_unconn, inp,
+ inp_unconn_list);
+ INP_HASH_WUNLOCK(inp->inp_pcbinfo);
- /* XXX-MJ torn writes are visible to SMR lookup */
- memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
- memset(&inp->in6p_faddr, 0, sizeof(inp->in6p_faddr));
- inp->inp_fport = 0;
+ if ((inp->inp_socket->so_proto->pr_flags & PR_CONNREQUIRED) == 0) {
+ /* See the comment in in_pcbinshash(). */
+ inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
+ /* XXX-MJ torn writes are visible to SMR lookup */
+ memset(&inp->in6p_faddr, 0, sizeof(inp->in6p_faddr));
+ inp->inp_fport = 0;
+ }
/* clear flowinfo - draft-itojun-ipv6-flowlabel-api-00 */
inp->inp_flow &= ~IPV6_FLOWLABEL_MASK;
}
@@ -879,7 +910,7 @@ in6_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
NET_EPOCH_ASSERT();
hdr = &pcbinfo->ipi_lbgrouphashbase[
- INP_PCBPORTHASH(lport, pcbinfo->ipi_lbgrouphashmask)];
+ INP_PCBPORTHASH(lport, pcbinfo->ipi_porthashmask)];
/*
* Search for an LB group match based on the following criteria:
diff --git a/sys/netinet6/ip6_fastfwd.c b/sys/netinet6/ip6_fastfwd.c
index 9298b8ea9a49..8a288eb19891 100644
--- a/sys/netinet6/ip6_fastfwd.c
+++ b/sys/netinet6/ip6_fastfwd.c
@@ -113,7 +113,7 @@ ip6_tryforward(struct mbuf *m)
IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) ||
- in6_localip(&ip6->ip6_dst))
+ in6_localip_fib(&ip6->ip6_dst, M_GETFIB(m)))
return (m);
/*
* Check that the amount of data in the buffers
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
index 9ae0d699ca31..0e8a0aa6f1ea 100644
--- a/sys/netinet6/ip6_mroute.c
+++ b/sys/netinet6/ip6_mroute.c
@@ -1191,6 +1191,10 @@ X_ip6_mforward(struct ip6_hdr *ip6, struct ifnet *ifp, struct mbuf *m)
mfct = &V_mfctables[M_GETFIB(m)];
MFC6_LOCK();
+ if (__predict_false(mfct->router == NULL)) {
+ MFC6_UNLOCK();
+ return (EADDRNOTAVAIL);
+ }
/*
* Determine forwarding mifs from the forwarding cache table
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 29374a39e336..359bfdd336e9 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -1698,10 +1698,6 @@ do { \
break;
}
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- INP_WUNLOCK(inp);
- return (ECONNRESET);
- }
optp = &inp->in6p_outputopts;
error = ip6_pcbopt(IPV6_HOPLIMIT,
(u_char *)&optval, sizeof(optval),
@@ -1827,10 +1823,6 @@ do { \
{
struct ip6_pktopts **optp;
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- INP_WUNLOCK(inp);
- return (ECONNRESET);
- }
optp = &inp->in6p_outputopts;
error = ip6_pcbopt(optname,
(u_char *)&optval, sizeof(optval),
@@ -1919,10 +1911,6 @@ do { \
optlen = sopt->sopt_valsize;
optbuf = optbuf_storage;
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
- INP_WUNLOCK(inp);
- return (ECONNRESET);
- }
optp = &inp->in6p_outputopts;
error = ip6_pcbopt(optname, optbuf, optlen,
optp, (td != NULL) ? td->td_ucred : NULL,
@@ -2410,11 +2398,6 @@ ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt,
optdata = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK); \
malloc_optdata = true; \
INP_RLOCK(inp); \
- if (inp->inp_flags & INP_DROPPED) { \
- INP_RUNLOCK(inp); \
- free(optdata, M_TEMP); \
- return (ECONNRESET); \
- } \
pktopt = inp->in6p_outputopts; \
if (pktopt && pktopt->field) { \
optdatalen = min(lenexpr, sopt->sopt_valsize); \
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 48e80bb75e0b..c531d11f9e62 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -513,6 +513,14 @@ nd6_options(union nd_opts *ndopts)
ndopts->nd_opts_pi_end =
(struct nd_opt_prefix_info *)nd_opt;
break;
+ case ND_OPT_ROUTE_INFO:
+ if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) {
+ ndopts->nd_opt_array[nd_opt->nd_opt_type]
+ = nd_opt;
+ }
+ ndopts->nd_opts_rti_end =
+ (struct nd_opt_route_info *)nd_opt;
+ break;
/* What about ND_OPT_ROUTE_INFO? RFC 4191 */
case ND_OPT_RDNSS: /* RFC 6106 */
case ND_OPT_DNSSL: /* RFC 6106 */
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 8c069c294593..8dc27aef5e12 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -74,12 +74,6 @@ struct llentry;
#define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */
#define ND6_IFF_NO_DAD 0x100
#define ND6_IFF_STABLEADDR 0x800
-#ifdef EXPERIMENTAL
-/* XXX: not related to ND. */
-#define ND6_IFF_IPV6_ONLY 0x200 /* draft-ietf-6man-ipv6only-flag */
-#define ND6_IFF_IPV6_ONLY_MANUAL 0x400
-#define ND6_IFF_IPV6_ONLY_MASK (ND6_IFF_IPV6_ONLY|ND6_IFF_IPV6_ONLY_MANUAL)
-#endif
struct in6_nbrinfo {
char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
@@ -293,7 +287,7 @@ VNET_DECLARE(int, ip6_temp_regen_advance); /* seconds */
#define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance)
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */
+ struct nd_opt_hdr *nd_opt_array[24]; /* max = ND_OPT_ROUTE_INFO */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -311,10 +305,20 @@ union nd_opts {
struct nd_opt_hdr *__res13;
struct nd_opt_nonce *nonce;
struct nd_opt_hdr *__res15;
+ struct nd_opt_hdr *__res16;
+ struct nd_opt_hdr *__res17;
+ struct nd_opt_hdr *__res18;
+ struct nd_opt_hdr *__res19;
+ struct nd_opt_hdr *__res20;
+ struct nd_opt_hdr *__res21;
+ struct nd_opt_hdr *__res22;
+ struct nd_opt_hdr *__res23;
+ struct nd_opt_route_info *rti_beg;
struct nd_opt_hdr *search; /* multiple opts */
struct nd_opt_hdr *last; /* multiple opts */
int done;
struct nd_opt_prefix_info *pi_end;/* multiple opts, end */
+ struct nd_opt_route_info *rti_end;/* multiple opts, end */
} nd_opt_each;
};
#define nd_opts_src_lladdr nd_opt_each.src_lladdr
@@ -324,6 +328,8 @@ union nd_opts {
#define nd_opts_rh nd_opt_each.rh
#define nd_opts_mtu nd_opt_each.mtu
#define nd_opts_nonce nd_opt_each.nonce
+#define nd_opts_rti nd_opt_each.rti_beg
+#define nd_opts_rti_end nd_opt_each.rti_end
#define nd_opts_search nd_opt_each.search
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 6f415408daae..623bfd60918e 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -60,6 +60,7 @@
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/route/route_ctl.h>
+#include <net/route/route_var.h>
#include <net/radix.h>
#include <net/vnet.h>
@@ -77,9 +78,20 @@
MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery");
+struct nd_routectl {
+ struct ifnet *ndrt_ifp; /* nexthop interface */
+ struct sockaddr_in6 ndrt_gateway; /* gateway address */
+ struct sockaddr_in6 ndrt_prefix; /* route prefix */
+ struct sockaddr_in6 ndrt_mask; /* route prefix mask */
+ uint8_t ndrt_plen; /* route prefix len */
+ uint8_t ndrt_flags; /* route info flags */
+ uint32_t ndrt_lifetime; /* route info lifetime (sec) */
+};
+
static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
bool, int);
+static int nd6_routelist_update(struct nd_routectl *);
static int nd6_prefix_onlink(struct nd_prefix *);
static int in6_get_tmp_ifid(struct in6_aliasreq *);
@@ -104,16 +116,6 @@ VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME;
VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE;
-#ifdef EXPERIMENTAL
-VNET_DEFINE_STATIC(int, nd6_ignore_ipv6_only_ra) = 1;
-#define V_nd6_ignore_ipv6_only_ra VNET(nd6_ignore_ipv6_only_ra)
-SYSCTL_INT(_net_inet6_icmp6, OID_AUTO,
- nd6_ignore_ipv6_only_ra, CTLFLAG_VNET | CTLFLAG_RW,
- &VNET_NAME(nd6_ignore_ipv6_only_ra), 0,
- "Ignore the 'IPv6-Only flag' in RA messages in compliance with "
- "draft-ietf-6man-ipv6only-flag");
-#endif
-
/* RTPREF_MEDIUM has to be 0! */
#define RTPREF_HIGH 1
#define RTPREF_MEDIUM 0
@@ -248,97 +250,6 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len)
m_freem(m);
}
-#ifdef EXPERIMENTAL
-/*
- * An initial update routine for draft-ietf-6man-ipv6only-flag.
- * We need to iterate over all default routers for the given
- * interface to see whether they are all advertising the "S"
- * (IPv6-Only) flag. If they do set, otherwise unset, the
- * interface flag we later use to filter on.
- *
- * XXXGL: The use of IF_ADDR_WLOCK (previously it was IF_AFDATA_LOCK) in this
- * function is quite strange.
- */
-static void
-defrtr_ipv6_only_ifp(struct ifnet *ifp)
-{
- struct nd_defrouter *dr;
- bool ipv6_only, ipv6_only_old;
-#ifdef INET
- struct epoch_tracker et;
- struct ifaddr *ifa;
- bool has_ipv4_addr;
-#endif
-
- if (V_nd6_ignore_ipv6_only_ra != 0)
- return;
-
- ipv6_only = true;
- ND6_RLOCK();
- TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry)
- if (dr->ifp == ifp &&
- (dr->raflags & ND_RA_FLAG_IPV6_ONLY) == 0)
- ipv6_only = false;
- ND6_RUNLOCK();
-
- IF_ADDR_WLOCK(ifp);
- ipv6_only_old = ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY;
- IF_ADDR_WUNLOCK(ifp);
-
- /* If nothing changed, we have an early exit. */
- if (ipv6_only == ipv6_only_old)
- return;
-
-#ifdef INET
- /*
- * Should we want to set the IPV6-ONLY flag, check if the
- * interface has a non-0/0 and non-link-local IPv4 address
- * configured on it. If it has we will assume working
- * IPv4 operations and will clear the interface flag.
- */
- has_ipv4_addr = false;
- if (ipv6_only) {
- NET_EPOCH_ENTER(et);
- CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- if (ifa->ifa_addr->sa_family != AF_INET)
- continue;
- if (in_canforward(
- satosin(ifa->ifa_addr)->sin_addr)) {
- has_ipv4_addr = true;
- break;
- }
- }
- NET_EPOCH_EXIT(et);
- }
- if (ipv6_only && has_ipv4_addr) {
- log(LOG_NOTICE, "%s rcvd RA w/ IPv6-Only flag set but has IPv4 "
- "configured, ignoring IPv6-Only flag.\n", ifp->if_xname);
- ipv6_only = false;
- }
-#endif
-
- IF_ADDR_WLOCK(ifp);
- if (ipv6_only)
- ifp->if_inet6->nd_flags |= ND6_IFF_IPV6_ONLY;
- else
- ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY;
- IF_ADDR_WUNLOCK(ifp);
-
-#ifdef notyet
- /* Send notification of flag change. */
-#endif
-}
-
-static void
-defrtr_ipv6_only_ipf_down(struct ifnet *ifp)
-{
-
- IF_ADDR_WLOCK(ifp);
- ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY;
- IF_ADDR_WUNLOCK(ifp);
-}
-#endif /* EXPERIMENTAL */
-
void
nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate)
{
@@ -349,10 +260,6 @@ nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate)
* unreachable but a different interface might still have connectivity.
*/
-#ifdef EXPERIMENTAL
- if (linkstate == LINK_STATE_DOWN)
- defrtr_ipv6_only_ipf_down(ifp);
-#endif
}
static void
@@ -410,6 +317,116 @@ nd6_ra_opt_pi(struct nd_opt_hdr *pt, struct ifnet *ifp,
}
static void
+nd6_ra_opt_rti(struct nd_opt_hdr *hdr, struct ifnet *ifp,
+ struct in6_addr saddr6, struct nd_defrouter *dr)
+{
+ struct nd_opt_route_info *rti = NULL;
+ struct nd_routectl rt;
+ struct in6_addr prefix, netmask;
+ char ip6bufs[INET6_ADDRSTRLEN];
+ int olen;
+ uint8_t pref;
+
+ if (hdr->nd_opt_type != ND_OPT_ROUTE_INFO)
+ return;
+
+ /* RFC 4191 section 2.3, Field validation */
+ rti = (struct nd_opt_route_info *)hdr;
+ if (rti->nd_opt_rti_len == 0 || rti->nd_opt_rti_len > 3) {
+ nd6log((LOG_INFO,
+ "%s: invalid option len %d for route "
+ "information option, ignored\n", __func__,
+ rti->nd_opt_rti_len));
+ return;
+ }
+ if (rti->nd_opt_rti_prefixlen > 128 ||
+ (rti->nd_opt_rti_prefixlen > 64 && rti->nd_opt_rti_len != 3) ||
+ (rti->nd_opt_rti_prefixlen > 0 && rti->nd_opt_rti_len < 2)) {
+ nd6log((LOG_INFO,
+ "%s: invalid prefix len %d with option len %d for route "
+ "information option, ignored\n", __func__,
+ rti->nd_opt_rti_prefixlen, rti->nd_opt_rti_len));
+ return;
+ }
+ pref = rti->nd_opt_rti_flags & ND_OPT_RTI_FLAG_PRF_MASK;
+ if ((pref & ND_RA_FLAG_RTPREF_RSV) != 0) {
+ nd6log((LOG_INFO,
+ "%s: reserved preference %d for route "
+ "information option, ignored\n", __func__,
+ pref));
+ return;
+ }
+
+ /*
+ * RFC 4191 section 2.3: The bits in the prefix after
+ * the prefix length (if any) are reserved and MUST be
+ * initialized to zero by the sender and ignored by the receiver.
+ */
+ memset(&prefix, 0, sizeof(prefix));
+ /*
+ * Calculate the variable length of the option and copy the remaining
+ * length into the prefix.
+ * The resulting value cannot exceed the size of struct in6_addr
+ * since the option length is limited to 3 (24 bytes).
+ */
+ olen = (rti->nd_opt_rti_len << 3) - sizeof(struct nd_opt_route_info);
+ memcpy(&prefix, (char *)rti + sizeof(struct nd_opt_route_info), olen);
+ in6_prefixlen2mask(&netmask, rti->nd_opt_rti_prefixlen);
+ IN6_MASK_ADDR(&prefix, &netmask);
+ if (IN6_IS_ADDR_MULTICAST(&prefix) || IN6_IS_ADDR_LINKLOCAL(&prefix)) {
+ nd6log((LOG_INFO, "%s: invalid prefix %s, ignored\n",
+ __func__, ip6_sprintf(ip6bufs, &prefix)));
+ return;
+ }
+
+ /*
+ * RFC 4191 section 3.1: The Router Preference and Lifetime
+ * values in a ::/0 Route Information Option override the
+ * preference and lifetime values in the Router Advertisement header.
+ */
+ if (rti->nd_opt_rti_prefixlen == 0 && dr != NULL) {
+ /*
+ * We may disable routes from RA messages when
+ * ND6_IFF_NO_RADR enabled on the receiving interface or
+ * (ip6.forwarding == 1 && ip6.rfc6204w3 != 1).
+ * Therefore, don't overwrite it.
+ */
+ if (dr->rtlifetime == 0)
+ return;
+ /*
+ * XXXPO: ignore rti lifetime bigger than uint16_max until
+ * we update the dr structure size to uint32_t.
+ */
+ if (ntohl(rti->nd_opt_rti_lifetime) > UINT16_MAX)
+ return;
+ dr->rtlifetime = ntohl(rti->nd_opt_rti_lifetime);
+ dr->expire = time_uptime + dr->rtlifetime;
+ dr->raflags &= ~ND_OPT_RTI_FLAG_PRF_MASK;
+ dr->raflags |= pref;
+ nd6log((LOG_INFO,
+ "%s: override default route with route information option\n",
+ __func__));
+ return;
+ }
+
+ memset(&rt, 0, sizeof(rt));
+ rt.ndrt_ifp = ifp;
+ rt.ndrt_prefix.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_prefix.sin6_family = AF_INET6;
+ rt.ndrt_prefix.sin6_addr = prefix;
+ rt.ndrt_gateway.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_gateway.sin6_family = AF_INET6;
+ rt.ndrt_gateway.sin6_addr = saddr6;
+ rt.ndrt_mask.sin6_len = sizeof(struct sockaddr_in6);
+ rt.ndrt_mask.sin6_family = AF_INET6;
+ rt.ndrt_mask.sin6_addr = netmask;
+ rt.ndrt_plen = rti->nd_opt_rti_prefixlen;
+ rt.ndrt_flags = rti->nd_opt_rti_flags;
+ rt.ndrt_lifetime = ntohl(rti->nd_opt_rti_lifetime);
+ (void)nd6_routelist_update(&rt);
+}
+
+static void
nd6_ra_opt_mtu(struct nd_opt_mtu *optmtu, struct ifnet *ifp,
struct in6_addr saddr6)
{
@@ -490,7 +507,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
struct in6_ifextra *ndi;
struct ip6_hdr *ip6;
struct nd_router_advert *nd_ra;
- struct nd_opt_hdr *pt;
+ struct nd_opt_hdr *pt, *rti;
struct in6_addr saddr6;
struct nd_defrouter dr0, *dr;
union nd_opts ndopts;
@@ -598,10 +615,16 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
nd_ra->nd_ra_curhoplimit);
}
}
+ /* Route Information */
+ if (ndopts.nd_opts_rti != NULL) {
+ for (rti = (struct nd_opt_hdr *)ndopts.nd_opts_rti;
+ rti <= (struct nd_opt_hdr *)ndopts.nd_opts_rti_end;
+ rti = (struct nd_opt_hdr *)((char *)rti +
+ (rti->nd_opt_len << 3))) {
+ nd6_ra_opt_rti(rti, ifp, saddr6, &dr0);
+ }
+ }
dr = defrtrlist_update(&dr0);
-#ifdef EXPERIMENTAL
- defrtr_ipv6_only_ifp(ifp);
-#endif
/* Prefix Information */
if (ndopts.nd_opts_pi != NULL) {
/*
@@ -779,10 +802,6 @@ defrouter_del(struct nd_defrouter *dr)
if (dr->ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV)
rt6_flush(&dr->rtaddr, dr->ifp);
-#ifdef EXPERIMENTAL
- defrtr_ipv6_only_ifp(dr->ifp);
-#endif
-
if (dr->installed) {
deldr = dr;
defrouter_delreq(dr);
@@ -2280,6 +2299,210 @@ restart:
}
/*
+ * Install advertised route via default router.
+ */
+static int
+nd6_route_rtrequest(struct nd_routectl *key)
+{
+ struct rib_cmd_info rc;
+ struct rib_head *rnh;
+ struct nhop_object *nh;
+ struct ifaddr *ifa;
+ struct ifnet *ifp;
+ struct sockaddr *dst, *gw, *mask;
+ int flags, error;
+ time_t expire;
+
+ NET_EPOCH_ASSERT();
+
+ ifp = key->ndrt_ifp;
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ mask = (struct sockaddr *)&key->ndrt_mask;
+ flags = RTF_DYNAMIC | RTF_GATEWAY;
+ if (key->ndrt_plen == 128) {
+ flags |= RTF_HOST;
+ mask = NULL;
+ }
+
+ rnh = rt_tables_get_rnh_safe(key->ndrt_ifp->if_fib, AF_INET6);
+ if (rnh == NULL)
+ return (EINVAL);
+
+ /* Get the best ifa for the given interface and gateway. */
+ if ((ifa = ifaof_ifpforaddr(gw, ifp)) == NULL)
+ return (ENETUNREACH);
+
+ expire = time_second + key->ndrt_lifetime;
+ struct rt_metrics rmx = {
+ .rmx_expire = expire,
+ };
+ struct rt_addrinfo info = {
+ .rti_flags = flags,
+ .rti_info = {
+ [RTAX_DST] = dst,
+ [RTAX_GATEWAY] = gw,
+ [RTAX_NETMASK] = mask,
+ [RTAX_AUTHOR] = gw,
+ },
+ .rti_ifa = ifa,
+ .rti_ifp = ifp,
+ .rti_mflags = RTV_EXPIRE,
+ .rti_rmx = &rmx,
+ };
+ /* XXX: route preference */
+ struct route_nhop_data rnd = {
+ .rnd_weight = RT_DEFAULT_WEIGHT,
+ };
+
+ error = nhop_create_from_info(rnh, &info, &nh);
+ if (error == 0) {
+ nhop_set_origin(nh, NH_ORIGIN_REDIRECT);
+ rnd.rnd_nhop = nh;
+ error = rib_add_route_px(ifp->if_fib, dst, key->ndrt_plen, &rnd,
+ RTM_F_CREATE, &rc);
+ }
+ if (error != 0)
+ return (error);
+ RTSTAT_INC(rts_dynamic);
+
+ /* Send notification of a route addition to userland. */
+ rt_missmsg_fib(RTM_REDIRECT, &info, flags | RTF_UP, error, ifp->if_fib);
+
+ return (error);
+}
+
+/*
+ * Update lifetime of advertised route.
+ */
+static int
+nd6_route_rtupdate(struct nd_routectl *key)
+{
+ struct rib_cmd_info rc;
+ time_t expire;
+
+ expire = time_second + key->ndrt_lifetime;
+ struct rt_metrics rmx = {
+ .rmx_expire = expire,
+ };
+ struct rt_addrinfo info = {
+ .rti_info[RTAX_DST] = (struct sockaddr *)&key->ndrt_prefix,
+ .rti_mflags = RTV_EXPIRE,
+ .rti_rmx = &rmx,
+ };
+ if (key->ndrt_plen != 128)
+ info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&key->ndrt_mask;
+ return (rib_change_route(key->ndrt_ifp->if_fib, &info, &rc));
+}
+
+/*
+ * Delete advertised route.
+ */
+static int
+nd6_route_rtdelete(struct nd_routectl *key)
+{
+ struct sockaddr *dst, *gw;
+ struct rib_cmd_info rc;
+ struct nhop_object *nh;
+ struct ifnet *ifp;
+ int error;
+
+ ifp = key->ndrt_ifp;
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ error = rib_del_route_px(ifp->if_fib, dst, key->ndrt_plen,
+ rib_match_gw, gw, 0, &rc);
+ if (error == 0) {
+ nh = nhop_select_func(rc.rc_nh_old, 0);
+ rt_routemsg(RTM_DELETE, rc.rc_rt, nh, ifp->if_fib);
+ }
+
+ return (error);
+}
+
+/*
+ * Lookup for exact match of advertised route with gateway
+ * as its nexthop address.
+ */
+static bool
+nd6_route_rtlookup(struct nd_routectl *key)
+{
+ RIB_RLOCK_TRACKER;
+ struct rib_head *rnh;
+ struct route_nhop_data rnd;
+ struct sockaddr *dst, *gw;
+
+ dst = (struct sockaddr *)&key->ndrt_prefix;
+ gw = (struct sockaddr *)&key->ndrt_gateway;
+ rnh = rt_tables_get_rnh_safe(key->ndrt_ifp->if_fib, AF_INET6);
+ if (rnh == NULL)
+ return (false);
+
+ RIB_RLOCK(rnh);
+ rib_lookup_prefix_plen(rnh, dst, key->ndrt_plen, &rnd);
+ RIB_RUNLOCK(rnh);
+ if (rnd.rnd_nhop == NULL)
+ return (false);
+
+ return (match_nhop_gw(rnd.rnd_nhop, gw));
+}
+
+static int
+nd6_routelist_update(struct nd_routectl *new)
+{
+ char ip6buf[INET6_ADDRSTRLEN];
+ int error = 0;
+
+ NET_EPOCH_ASSERT();
+
+ /*
+ * RFC 4191 section 3.1: If the received route's
+ * lifetime is zero, the route is removed from the Routing
+ * Table if present.
+ */
+ if (new->ndrt_lifetime == 0) {
+ error = nd6_route_rtdelete(new);
+ if (error != 0) {
+ nd6log((LOG_DEBUG,
+ "%s: failed to delete the route %s/%d on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ return (error);
+ }
+
+ if (nd6_route_rtlookup(new)) {
+ /*
+ * RFC 4191 section 3.1: the route's lifetime and
+ * preference is updated if the route is already present.
+ * XXX: preference on routing table? (ndrt_flags)
+ */
+ error = nd6_route_rtupdate(new);
+ if (error != 0) {
+ nd6log((LOG_DEBUG,
+ "%s: failed to update route lifetime of "
+ "%s/%d from RA on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ } else {
+ /*
+ * RFC 4191 section 3.1: If a route's lifetime is non-zero,
+ * the route is added to the Routing Table if not present.
+ */
+ error = nd6_route_rtrequest(new);
+ if (error != 0) {
+ nd6log((LOG_NOTICE,
+ "%s: failed to add route %s/%d from RA on %s (errno=%d)\n",
+ __func__, ip6_sprintf(ip6buf, &new->ndrt_prefix.sin6_addr),
+ new->ndrt_plen, if_name(new->ndrt_ifp), error));
+ }
+ }
+
+ return (error);
+}
+
+/*
* Get a randomized interface identifier for a temporary address
* Based on RFC 8981, Section 3.3.1.
*/
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 729be392668a..ede791a7d175 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -832,14 +832,9 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
laddr = &in6a;
if (inp->inp_lport == 0) {
- struct inpcbinfo *pcbinfo;
-
INP_WLOCK_ASSERT(inp);
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
- INP_HASH_WLOCK(pcbinfo);
error = in6_pcbsetport(laddr, inp, td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
if (error != 0) {
/* Undo an address bind that may have occurred. */
inp->in6p_laddr = in6addr_any;
@@ -986,9 +981,7 @@ static void
udp6_abort(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp6_abort: inp == NULL"));
@@ -1002,9 +995,8 @@ udp6_abort(struct socket *so)
#endif
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
- INP_HASH_WLOCK(pcbinfo);
in6_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
soisdisconnected(so);
}
INP_WUNLOCK(inp);
@@ -1050,11 +1042,9 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct sockaddr_in6 *sin6_p;
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
int error;
u_char vflagsav;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp6_bind: inp == NULL"));
@@ -1066,7 +1056,6 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
sin6_p = (struct sockaddr_in6 *)nam;
INP_WLOCK(inp);
- INP_HASH_WLOCK(pcbinfo);
vflagsav = inp->inp_vflag;
inp->inp_vflag &= ~INP_IPV4;
inp->inp_vflag |= INP_IPV6;
@@ -1095,7 +1084,6 @@ out:
#endif
if (error != 0)
inp->inp_vflag = vflagsav;
- INP_HASH_WUNLOCK(pcbinfo);
INP_WUNLOCK(inp);
return (error);
}
@@ -1104,9 +1092,7 @@ static void
udp6_close(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp6_close: inp == NULL"));
@@ -1119,9 +1105,8 @@ udp6_close(struct socket *so)
}
#endif
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
- INP_HASH_WLOCK(pcbinfo);
in6_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
soisdisconnected(so);
}
INP_WUNLOCK(inp);
@@ -1132,12 +1117,10 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
{
struct epoch_tracker et;
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
struct sockaddr_in6 *sin6;
int error;
u_char vflagsav;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp6_connect: inp == NULL"));
@@ -1175,9 +1158,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
inp->inp_vflag |= INP_IPV4;
inp->inp_vflag &= ~INP_IPV6;
NET_EPOCH_ENTER(et);
- INP_HASH_WLOCK(pcbinfo);
error = in_pcbconnect(inp, &sin, td->td_ucred);
- INP_HASH_WUNLOCK(pcbinfo);
NET_EPOCH_EXIT(et);
/*
* If connect succeeds, mark socket as connected. If
@@ -1208,9 +1189,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
inp->inp_vflag &= ~INP_IPV4;
inp->inp_vflag |= INP_IPV6;
NET_EPOCH_ENTER(et);
- INP_HASH_WLOCK(pcbinfo);
error = in6_pcbconnect(inp, sin6, td->td_ucred, true);
- INP_HASH_WUNLOCK(pcbinfo);
NET_EPOCH_EXIT(et);
/*
* If connect succeeds, mark socket as connected. If
@@ -1243,9 +1222,7 @@ static int
udp6_disconnect(struct socket *so)
{
struct inpcb *inp;
- struct inpcbinfo *pcbinfo;
- pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp6_disconnect: inp == NULL"));
@@ -1263,9 +1240,8 @@ udp6_disconnect(struct socket *so)
return (ENOTCONN);
}
- INP_HASH_WLOCK(pcbinfo);
in6_pcbdisconnect(inp);
- INP_HASH_WUNLOCK(pcbinfo);
+ memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
SOCK_LOCK(so);
so->so_state &= ~SS_ISCONNECTED; /* XXX */
SOCK_UNLOCK(so);
diff --git a/sys/netipsec/xform_tcp.c b/sys/netipsec/xform_tcp.c
index d3d4d6c4d734..a87b27048dd7 100644
--- a/sys/netipsec/xform_tcp.c
+++ b/sys/netipsec/xform_tcp.c
@@ -76,7 +76,7 @@
static int
tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
{
- struct tcpcb *tp;
+ struct tcpcb *tp = intotcpcb(inp);
int error, optval;
if (sopt->sopt_name != TCP_MD5SIG) {
@@ -85,11 +85,10 @@ tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
if (sopt->sopt_dir == SOPT_GET) {
INP_RLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_RUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
INP_RUNLOCK(inp);
@@ -103,11 +102,10 @@ tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
/* INP_WLOCK_RECHECK */
INP_WLOCK(inp);
- if (inp->inp_flags & INP_DROPPED) {
+ if (tp->t_flags & TF_DISCONNECTED) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
- tp = intotcpcb(inp);
if (optval > 0)
tp->t_flags |= TF_SIGNATURE;
else
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index 3e30d74a3793..d449e4114f24 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -556,10 +556,7 @@ rtnl_handle_dellink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
}
NLP_LOG(LOG_DEBUG3, nlp, "mapped ifindex %u to %s", attrs.ifi_index, if_name(ifp));
- sx_xlock(&ifnet_detach_sxlock);
error = if_clone_destroy(if_name(ifp));
- sx_xunlock(&ifnet_detach_sxlock);
-
NLP_LOG(LOG_DEBUG2, nlp, "deleting interface %s returned %d", if_name(ifp), error);
if_rele(ifp);
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
index 8b5189d1c588..0bd06ff1f6d5 100644
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -286,4 +286,48 @@ enum {
#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
+/* IFLA_INFO_DATA geneve attributes */
+enum {
+ IFLA_GENEVE_UNSPEC,
+ IFLA_GENEVE_ID,
+ IFLA_GENEVE_PROTOCOL,
+ IFLA_GENEVE_LOCAL,
+ IFLA_GENEVE_REMOTE,
+ IFLA_GENEVE_LOCAL_PORT,
+ IFLA_GENEVE_PORT,
+ IFLA_GENEVE_PORT_RANGE,
+ IFLA_GENEVE_DF,
+ IFLA_GENEVE_TTL,
+ IFLA_GENEVE_TTL_INHERIT,
+ IFLA_GENEVE_DSCP_INHERIT,
+ IFLA_GENEVE_COLLECT_METADATA,
+ IFLA_GENEVE_FTABLE_LEARN,
+ IFLA_GENEVE_FTABLE_FLUSH,
+ IFLA_GENEVE_FTABLE_MAX,
+ IFLA_GENEVE_FTABLE_TIMEOUT,
+ IFLA_GENEVE_FTABLE_COUNT,
+ IFLA_GENEVE_FTABLE_NOSPACE_CNT,
+ IFLA_GENEVE_FTABLE_LOCK_UP_FAIL_CNT,
+ IFLA_GENEVE_MC_IFNAME,
+ IFLA_GENEVE_MC_IFINDEX,
+ IFLA_GENEVE_TXCSUM_CNT,
+ IFLA_GENEVE_TSO_CNT,
+ IFLA_GENEVE_RXCSUM_CNT,
+ __IFLA_GENEVE_MAX,
+};
+#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
+
+enum ifla_geneve_df {
+ IFLA_GENEVE_DF_UNSET,
+ IFLA_GENEVE_DF_SET,
+ IFLA_GENEVE_DF_INHERIT,
+ __IFLA_GENEVE_DF_MAX,
+};
+#define IFLA_GENEVE_DF_MAX (__IFLA_GENEVE_DF_MAX - 1)
+
+struct ifla_geneve_port_range {
+ uint16_t low;
+ uint16_t high;
+};
+
#endif
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 90342f045763..4576ad6e41de 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -798,11 +798,11 @@ SYSCTL_NODE(_net, OID_AUTO, pf, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
VNET_DEFINE(u_long, pf_hashmask);
VNET_DEFINE(u_long, pf_srchashmask);
VNET_DEFINE(u_long, pf_udpendpointhashmask);
-VNET_DEFINE_STATIC(u_long, pf_hashsize);
+VNET_DEFINE_STATIC(u_long, pf_hashsize) = PF_HASHSIZ;
#define V_pf_hashsize VNET(pf_hashsize)
-VNET_DEFINE_STATIC(u_long, pf_srchashsize);
+VNET_DEFINE_STATIC(u_long, pf_srchashsize) = PF_SRCHASHSIZ;
#define V_pf_srchashsize VNET(pf_srchashsize)
-VNET_DEFINE_STATIC(u_long, pf_udpendpointhashsize);
+VNET_DEFINE_STATIC(u_long, pf_udpendpointhashsize) = PF_UDPENDHASHSIZ;
#define V_pf_udpendpointhashsize VNET(pf_udpendpointhashsize)
u_long pf_ioctl_maxcount = 65535;
@@ -1429,18 +1429,13 @@ pf_mtag_initialize(void)
void
pf_initialize(void)
{
- struct pf_keyhash *kh;
- struct pf_idhash *ih;
- struct pf_srchash *sh;
- struct pf_udpendpointhash *uh;
- u_int i;
-
- if (V_pf_hashsize == 0 || !powerof2(V_pf_hashsize))
- V_pf_hashsize = PF_HASHSIZ;
- if (V_pf_srchashsize == 0 || !powerof2(V_pf_srchashsize))
- V_pf_srchashsize = PF_SRCHASHSIZ;
- if (V_pf_udpendpointhashsize == 0 || !powerof2(V_pf_udpendpointhashsize))
- V_pf_udpendpointhashsize = PF_UDPENDHASHSIZ;
+ struct hashalloc_args ha = {
+ .mflags = M_NOWAIT, /* see bf56a3fe47ef4 and bug 209475 */
+ .mtype = M_PFHASH,
+ .type = HASH_TYPE_POWER2,
+ .head = HASH_HEAD_LIST,
+ .lock = HASH_LOCK_MTX,
+ };
V_pf_hashseed = arc4random();
@@ -1450,35 +1445,28 @@ pf_initialize(void)
V_pf_limits[PF_LIMIT_STATES].zone = V_pf_state_z;
uma_zone_set_max(V_pf_state_z, PFSTATE_HIWAT);
uma_zone_set_warning(V_pf_state_z, "PF states limit reached");
-
V_pf_state_key_z = uma_zcreate("pf state keys",
sizeof(struct pf_state_key), pf_state_key_ctor, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
-
- V_pf_keyhash = mallocarray(V_pf_hashsize, sizeof(struct pf_keyhash),
- M_PFHASH, M_NOWAIT | M_ZERO);
- V_pf_idhash = mallocarray(V_pf_hashsize, sizeof(struct pf_idhash),
- M_PFHASH, M_NOWAIT | M_ZERO);
+retry_waitok:
+ ha.size = V_pf_hashsize;
+ ha.lname = "pf_keyhash";
+ ha.lopts = MTX_DEF | MTX_DUPOK;
+ V_pf_keyhash = hashalloc(&ha);
+ ha.lname = "pf_idhash";
+ ha.lopts = MTX_DEF;
+ V_pf_idhash = hashalloc(&ha);
if (V_pf_keyhash == NULL || V_pf_idhash == NULL) {
printf("pf: Unable to allocate memory for "
"state_hashsize %lu.\n", V_pf_hashsize);
-
- free(V_pf_keyhash, M_PFHASH);
- free(V_pf_idhash, M_PFHASH);
-
+ hashfree(V_pf_keyhash, &ha);
+ hashfree(V_pf_idhash, &ha);
V_pf_hashsize = PF_HASHSIZ;
- V_pf_keyhash = mallocarray(V_pf_hashsize,
- sizeof(struct pf_keyhash), M_PFHASH, M_WAITOK | M_ZERO);
- V_pf_idhash = mallocarray(V_pf_hashsize,
- sizeof(struct pf_idhash), M_PFHASH, M_WAITOK | M_ZERO);
+ ha.mflags = M_WAITOK;
+ goto retry_waitok;
}
-
+ V_pf_hashsize = ha.size;
V_pf_hashmask = V_pf_hashsize - 1;
- for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash; i <= V_pf_hashmask;
- i++, kh++, ih++) {
- mtx_init(&kh->lock, "pf_keyhash", NULL, MTX_DEF | MTX_DUPOK);
- mtx_init(&ih->lock, "pf_idhash", NULL, MTX_DEF);
- }
/* Source nodes. */
V_pf_sources_z = uma_zcreate("pf source nodes",
@@ -1487,45 +1475,40 @@ pf_initialize(void)
V_pf_limits[PF_LIMIT_SRC_NODES].zone = V_pf_sources_z;
uma_zone_set_max(V_pf_sources_z, PFSNODE_HIWAT);
uma_zone_set_warning(V_pf_sources_z, "PF source nodes limit reached");
-
- V_pf_srchash = mallocarray(V_pf_srchashsize,
- sizeof(struct pf_srchash), M_PFHASH, M_NOWAIT | M_ZERO);
+ ha.size = V_pf_srchashsize;
+ ha.lname = "pf_srchash";
+ ha.lopts = MTX_DEF;
+ ha.mflags = M_NOWAIT;
+retry_waitok2:
+ V_pf_srchash = hashalloc(&ha);
if (V_pf_srchash == NULL) {
printf("pf: Unable to allocate memory for "
"source_hashsize %lu.\n", V_pf_srchashsize);
-
- V_pf_srchashsize = PF_SRCHASHSIZ;
- V_pf_srchash = mallocarray(V_pf_srchashsize,
- sizeof(struct pf_srchash), M_PFHASH, M_WAITOK | M_ZERO);
+ ha.size = PF_SRCHASHSIZ;
+ ha.mflags = M_WAITOK;
+ goto retry_waitok2;
}
-
+ V_pf_srchashmask = ha.size;
V_pf_srchashmask = V_pf_srchashsize - 1;
- for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++)
- mtx_init(&sh->lock, "pf_srchash", NULL, MTX_DEF);
-
/* UDP endpoint mappings. */
V_pf_udp_mapping_z = uma_zcreate("pf UDP mappings",
sizeof(struct pf_udp_mapping), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
- V_pf_udpendpointhash = mallocarray(V_pf_udpendpointhashsize,
- sizeof(struct pf_udpendpointhash), M_PFHASH, M_NOWAIT | M_ZERO);
+ ha.size = V_pf_udpendpointhashsize;
+ ha.lname = "pf_udpendpointhash";
+ ha.mflags = M_NOWAIT;
+retry_waitok3:
+ V_pf_udpendpointhash = hashalloc(&ha);
if (V_pf_udpendpointhash == NULL) {
printf("pf: Unable to allocate memory for "
"udpendpoint_hashsize %lu.\n", V_pf_udpendpointhashsize);
-
- V_pf_udpendpointhashsize = PF_UDPENDHASHSIZ;
- V_pf_udpendpointhash = mallocarray(V_pf_udpendpointhashsize,
- sizeof(struct pf_udpendpointhash), M_PFHASH, M_WAITOK | M_ZERO);
+ ha.size = PF_UDPENDHASHSIZ;
+ ha.mflags = M_WAITOK;
+ goto retry_waitok3;
}
-
+ V_pf_udpendpointhashsize = ha.size;
V_pf_udpendpointhashmask = V_pf_udpendpointhashsize - 1;
- for (i = 0, uh = V_pf_udpendpointhash;
- i <= V_pf_udpendpointhashmask;
- i++, uh++) {
- mtx_init(&uh->lock, "pf_udpendpointhash", NULL,
- MTX_DEF | MTX_DUPOK);
- }
/* Anchors */
V_pf_anchor_z = uma_zcreate("pf anchors",
@@ -1590,41 +1573,20 @@ pf_mtag_cleanup(void)
void
pf_cleanup(void)
{
- struct pf_keyhash *kh;
- struct pf_idhash *ih;
- struct pf_srchash *sh;
- struct pf_udpendpointhash *uh;
+ struct hashalloc_args ha = {
+ .size = V_pf_hashsize,
+ .mtype = M_PFHASH,
+ .head = HASH_HEAD_LIST,
+ .lock = HASH_LOCK_MTX,
+ };
struct pf_send_entry *pfse, *next;
- u_int i;
- for (i = 0, kh = V_pf_keyhash, ih = V_pf_idhash;
- i <= V_pf_hashmask;
- i++, kh++, ih++) {
- KASSERT(LIST_EMPTY(&kh->keys), ("%s: key hash not empty",
- __func__));
- KASSERT(LIST_EMPTY(&ih->states), ("%s: id hash not empty",
- __func__));
- mtx_destroy(&kh->lock);
- mtx_destroy(&ih->lock);
- }
- free(V_pf_keyhash, M_PFHASH);
- free(V_pf_idhash, M_PFHASH);
-
- for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask; i++, sh++) {
- KASSERT(LIST_EMPTY(&sh->nodes),
- ("%s: source node hash not empty", __func__));
- mtx_destroy(&sh->lock);
- }
- free(V_pf_srchash, M_PFHASH);
-
- for (i = 0, uh = V_pf_udpendpointhash;
- i <= V_pf_udpendpointhashmask;
- i++, uh++) {
- KASSERT(LIST_EMPTY(&uh->endpoints),
- ("%s: udp endpoint hash not empty", __func__));
- mtx_destroy(&uh->lock);
- }
- free(V_pf_udpendpointhash, M_PFHASH);
+ hashfree(V_pf_keyhash, &ha);
+ hashfree(V_pf_idhash, &ha);
+ ha.size = V_pf_srchashsize;
+ hashfree(V_pf_srchash, &ha);
+ ha.size = V_pf_udpendpointhashsize;
+ hashfree(V_pf_udpendpointhash, &ha);
STAILQ_FOREACH_SAFE(pfse, &V_pf_sendqueue, pfse_next, next) {
m_freem(pfse->pfse_m);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index a2e12b3065d0..8b2ff108d9cb 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2239,7 +2239,7 @@ pf_sourcelim_add(const struct pfioc_sourcelim *ioc)
if (RB_INSERT(pf_sourcelim_nm_tree, &V_pf_sourcelim_nm_tree_inactive,
pfsrlim) != NULL) {
- RB_INSERT(pf_sourcelim_nm_tree, &V_pf_sourcelim_nm_tree_inactive,
+ RB_REMOVE(pf_sourcelim_nm_tree, &V_pf_sourcelim_nm_tree_inactive,
pfsrlim);
error = EBUSY;
goto unlock;
@@ -2252,6 +2252,8 @@ pf_sourcelim_add(const struct pfioc_sourcelim *ioc)
return (0);
unlock:
+ if (pfsrlim->pfsrlim_overload.table != NULL)
+ pfr_detach_table(pfsrlim->pfsrlim_overload.table);
PF_RULES_WUNLOCK();
free:
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index 1362ce5387bb..348c1cafbd49 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -236,10 +236,10 @@ pf_frnode_compare(struct pf_frnode *a, struct pf_frnode *b)
static __inline int
pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b)
{
- int diff;
-
- if ((diff = a->fr_id - b->fr_id) != 0)
- return (diff);
+ if (a->fr_id > b->fr_id)
+ return (1);
+ if (a->fr_id < b->fr_id)
+ return (-1);
return (0);
}
diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c
index 650334c45db3..e6290e622fea 100644
--- a/sys/netpfil/pf/pf_table.c
+++ b/sys/netpfil/pf/pf_table.c
@@ -2423,7 +2423,7 @@ _next_block:
_next_entry:
/* we need to increase the counter past the nested block */
- pfr_prepare_network(&umask, AF_INET, ke2->pfrke_net);
+ pfr_prepare_network(&umask, af, ke2->pfrke_net);
pfr_sockaddr_to_pf_addr(&umask, &umask_addr);
pf_poolmask(addr, addr, &umask_addr, &pfr_ffaddr, af);
pf_addr_inc(addr, af);
diff --git a/sys/powerpc/booke/pmap.c b/sys/powerpc/booke/pmap.c
index 08516b151e6b..a76ef6a089fd 100644
--- a/sys/powerpc/booke/pmap.c
+++ b/sys/powerpc/booke/pmap.c
@@ -177,6 +177,7 @@ static struct mtx copy_page_mutex;
#endif
static struct mtx tlbivax_mutex;
+static bool mmuv2;
/**************************************************************************/
/* PMAP */
@@ -640,6 +641,9 @@ mmu_booke_bootstrap(vm_offset_t start, vm_offset_t kernelend)
debugf("mmu_booke_bootstrap: entered\n");
+ if ((mfspr(SPR_MMUCFG) & MMUCFG_MAVN_M) > 0)
+ mmuv2 = true;
+
/* Set interesting system properties */
#ifdef __powerpc64__
hw_direct_map = 1;
@@ -2703,7 +2707,7 @@ tsize2size(unsigned int tsize)
* size = 4^tsize * 2^10 = 2^(2 * tsize - 10)
*/
- return ((1 << (2 * tsize)) * 1024);
+ return ((1UL << tsize) * 1024);
}
/*
@@ -2713,7 +2717,7 @@ static unsigned int
size2tsize(vm_size_t size)
{
- return (ilog2(size) / 2 - 5);
+ return (ilog2(size) - 10);
}
/*
@@ -2772,23 +2776,29 @@ tlb1_mapin_region(vm_offset_t va, vm_paddr_t pa, vm_size_t size, int wimge)
{
vm_offset_t base;
vm_size_t mapped, sz, ssize;
+ int shift;
mapped = 0;
base = va;
ssize = size;
+ if (mmuv2)
+ shift = 1;
+ else
+ shift = 2;
+
while (size > 0) {
- sz = 1UL << (ilog2(size) & ~1);
+ sz = 1UL << (ilog2(size) & ~(shift - 1));
/* Align size to PA */
if (pa % sz != 0) {
do {
- sz >>= 2;
+ sz >>= shift;
} while (pa % sz != 0);
}
/* Now align from there to VA */
if (va % sz != 0) {
do {
- sz >>= 2;
+ sz >>= shift;
} while (va % sz != 0);
}
#ifdef __powerpc64__
@@ -2805,7 +2815,8 @@ tlb1_mapin_region(vm_offset_t va, vm_paddr_t pa, vm_size_t size, int wimge)
* For now, though, since we have plenty of space in TLB1,
* always avoid creating entries larger than 4GB.
*/
- sz = MIN(sz, 1UL << 32);
+ if (!mmuv2)
+ sz = MIN(sz, 1UL << 32);
#endif
if (bootverbose)
printf("Wiring VA=%p to PA=%jx (size=%lx)\n",
diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64
index 8daf5353263a..f2c688b1c622 100644
--- a/sys/powerpc/conf/GENERIC64
+++ b/sys/powerpc/conf/GENERIC64
@@ -175,6 +175,8 @@ device glc # Sony Playstation 3 Ethernet
device llan # IBM pSeries Virtual Ethernet
device cxgbe # Chelsio 10/25G NIC
+device mdio # MDIO bus
+
# PCI Ethernet NICs that use the common MII bus controller code.
device miibus # MII bus support
device bge # Broadcom BCM570xx Gigabit Ethernet
diff --git a/sys/powerpc/conf/GENERIC64LE b/sys/powerpc/conf/GENERIC64LE
index 499ee95d1905..ee5ecc271743 100644
--- a/sys/powerpc/conf/GENERIC64LE
+++ b/sys/powerpc/conf/GENERIC64LE
@@ -181,6 +181,8 @@ device fxp # Intel EtherExpress PRO/100B (82557, 82558)
device re # Realtek 8139C+/8169/8169S/8110S
device rl # Realtek 8129/8139
+device mdio # MDIO bus
+
# Nvidia/Mellanox Connect-X 4 and later, Ethernet only
# mlx5ib requires ibcore infra and is not included by default
device mlx5 # Base driver
diff --git a/sys/powerpc/include/spr.h b/sys/powerpc/include/spr.h
index 605b1be194d9..5c6e9d67fcb4 100644
--- a/sys/powerpc/include/spr.h
+++ b/sys/powerpc/include/spr.h
@@ -864,5 +864,10 @@
#define BUCSR_BPEN 0x00000001 /* Branch Prediction Enable */
#define BUCSR_BBFI 0x00000200 /* Branch Buffer Flash Invalidate */
+#define SPR_MMUCFG 0x3f7 /* ..8 MMU Configuration Register */
+#define MMUCFG_PIDSIZE_M 0x000007c0
+#define MMUCFG_PIDSIZE_S 6
+#define MMUCFG_MAVN_M 0x00000003
+
#endif /* BOOKE */
#endif /* !_POWERPC_SPR_H_ */
diff --git a/sys/powerpc/include/tlb.h b/sys/powerpc/include/tlb.h
index 0a4463e0b928..33d31efab604 100644
--- a/sys/powerpc/include/tlb.h
+++ b/sys/powerpc/include/tlb.h
@@ -50,20 +50,20 @@
#define MAS1_TID_SHIFT 16
#define MAS1_TS_MASK 0x00001000
#define MAS1_TS_SHIFT 12
-#define MAS1_TSIZE_MASK 0x00000F00
-#define MAS1_TSIZE_SHIFT 8
+#define MAS1_TSIZE_MASK 0x00000F80
+#define MAS1_TSIZE_SHIFT 7
-#define TLB_SIZE_4K 1
-#define TLB_SIZE_16K 2
-#define TLB_SIZE_64K 3
-#define TLB_SIZE_256K 4
-#define TLB_SIZE_1M 5
-#define TLB_SIZE_4M 6
-#define TLB_SIZE_16M 7
-#define TLB_SIZE_64M 8
-#define TLB_SIZE_256M 9
-#define TLB_SIZE_1G 10
-#define TLB_SIZE_4G 11
+#define TLB_SIZE_4K 2
+#define TLB_SIZE_16K 4
+#define TLB_SIZE_64K 6
+#define TLB_SIZE_256K 8
+#define TLB_SIZE_1M 10
+#define TLB_SIZE_4M 12
+#define TLB_SIZE_16M 14
+#define TLB_SIZE_64M 16
+#define TLB_SIZE_256M 18
+#define TLB_SIZE_1G 20
+#define TLB_SIZE_4G 22
#ifdef __powerpc64__
#define MAS2_EPN_MASK 0xFFFFFFFFFFFFF000UL
diff --git a/sys/powerpc/mpc85xx/platform_mpc85xx.c b/sys/powerpc/mpc85xx/platform_mpc85xx.c
index cc2ad829eb05..6781fdffa5a9 100644
--- a/sys/powerpc/mpc85xx/platform_mpc85xx.c
+++ b/sys/powerpc/mpc85xx/platform_mpc85xx.c
@@ -581,9 +581,19 @@ dummy_freeze(device_t dev, bool freeze)
/* QorIQ Run control/power management timebase management. */
-#define RCPM_CTBENR 0x00000084
+#define RCPM_CTBENR_1_0 0x00000084
+#define RCPM_CTBENR_2_0 0x000001a0
+
struct mpc85xx_rcpm_softc {
struct resource *sc_mem;
+ bus_addr_t sc_ctbenr;
+ uint32_t sc_saved_tbenr;
+};
+
+struct ofw_compat_data compats[] = {
+ { "fsl,qoriq-rcpm-1.0", RCPM_CTBENR_1_0 },
+ { "fsl,qoriq-rcpm-2.0", RCPM_CTBENR_2_0 },
+ { NULL, 0 }
};
static void
@@ -593,16 +603,17 @@ mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze)
sc = device_get_softc(dev);
- if (freeze)
- bus_write_4(sc->sc_mem, RCPM_CTBENR, 0);
- else
- bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1);
+ if (freeze) {
+ sc->sc_saved_tbenr = bus_read_4(sc->sc_mem, sc->sc_ctbenr);
+ bus_write_4(sc->sc_mem, sc->sc_ctbenr, 0);
+ } else
+ bus_write_4(sc->sc_mem, sc->sc_ctbenr, sc->sc_saved_tbenr);
}
static int
mpc85xx_rcpm_probe(device_t dev)
{
- if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0"))
+ if (ofw_bus_search_compatible(dev, compats)->ocd_str == NULL)
return (ENXIO);
device_set_desc(dev, "QorIQ Run control and power management");
@@ -622,6 +633,7 @@ mpc85xx_rcpm_attach(device_t dev)
rid = 0;
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE | RF_SHAREABLE);
+ sc->sc_ctbenr = ofw_bus_search_compatible(dev, compats)->ocd_data;
return (0);
}
diff --git a/sys/powerpc/powerpc/intr_machdep.c b/sys/powerpc/powerpc/intr_machdep.c
index daab391bb800..7e742845dbd3 100644
--- a/sys/powerpc/powerpc/intr_machdep.c
+++ b/sys/powerpc/powerpc/intr_machdep.c
@@ -537,7 +537,7 @@ powerpc_setup_intr_int(const char *name, u_int irq, driver_filter_t filter,
if (error)
return (error);
i->pi_domain = domain;
- if (strcmp(name, "IPI") != 0) {
+ if (ipi) {
CPU_ZERO(&i->pi_cpuset);
CPU_COPY(&cpuset_domain[domain], &i->pi_cpuset);
}
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
index 8f2fc1e2debb..0b13241f0ee3 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -171,6 +171,12 @@ typedef __pid_t pid_t;
#define FOPENFAILED O_TTY_INIT
/* Only for O_PATH files which passed ACCESS FREAD check on open */
#define FKQALLOWED O_RESOLVE_BENEATH
+/* Flags userspace is allowed to pass to openat() */
+#define FUSERALLOWED (O_ACCMODE | O_NONBLOCK | O_APPEND | O_SHLOCK | \
+ O_EXLOCK | O_ASYNC | O_SYNC | O_NOFOLLOW | O_CREAT | O_TRUNC | \
+ O_EXCL | O_NOCTTY | O_DIRECT | O_DIRECTORY | O_EXEC | O_TTY_INIT | \
+ O_CLOEXEC | O_VERIFY | O_PATH | O_RESOLVE_BENEATH | O_DSYNC | \
+ O_EMPTY_PATH | O_NAMEDATTR | O_CLOFORK)
/* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */
#define FFLAGS(oflags) ((oflags) & O_EXEC ? (oflags) : (oflags) + 1)
diff --git a/sys/sys/hash.h b/sys/sys/hash.h
index ac4bb35d70e9..5266632b1339 100644
--- a/sys/sys/hash.h
+++ b/sys/sys/hash.h
@@ -121,6 +121,43 @@ hash32_strne(const void *buf, size_t len, int end, const char **ep,
}
#ifdef _KERNEL
+struct hashalloc_args {
+ u_int version; /* for extendability, now 0 */
+ int error; /* out: error on failure */
+ size_t size; /* in: wanted, out: allocated */
+ size_t hdrsize; /* size of bucket header, 0 = auto */
+ enum {
+ HASH_TYPE_POWER2 = 0,
+ HASH_TYPE_PRIME,
+ } type;
+ enum {
+ HASH_HEAD_LIST = 0,
+ HASH_HEAD_CK_LIST,
+ HASH_HEAD_SLIST,
+ HASH_HEAD_CK_SLIST,
+ HASH_HEAD_STAILQ,
+ HASH_HEAD_CK_STAILQ,
+ HASH_HEAD_TAILQ,
+ } head;
+ enum {
+ HASH_LOCK_NONE = 0,
+ HASH_LOCK_MTX,
+ HASH_LOCK_RWLOCK,
+ HASH_LOCK_SX,
+ HASH_LOCK_RMLOCK,
+ HASH_LOCK_RMSLOCK,
+ } lock;
+ int mflags; /* malloc(9) flags */
+ int lopts; /* lock opts */
+ struct malloc_type *mtype; /* malloc(9) type */
+ const char *lname; /* lock name */
+ int (*ctor)(void *); /* bucket constructor */
+ void (*dtor)(void *); /* bucket destructor */
+};
+
+void *hashalloc(struct hashalloc_args *);
+void hashfree(void *, struct hashalloc_args *);
+
/*
* Hashing function from Bob Jenkins. Implementation in libkern/jenkins_hash.c.
*/
diff --git a/sys/sys/kobj.h b/sys/sys/kobj.h
index 7adc01c1f1f3..7705f609bea9 100644
--- a/sys/sys/kobj.h
+++ b/sys/sys/kobj.h
@@ -29,6 +29,8 @@
#ifndef _SYS_KOBJ_H_
#define _SYS_KOBJ_H_
+#include <sys/types.h>
+
/*
* Forward declarations
*/
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index 1e09891d259f..c5072cec904b 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -680,7 +680,7 @@ m_epg_pagelen(const struct mbuf *m, int pidx, int pgoff)
#define CSUM_INNER_IP_TSO 0x00020000
#define CSUM_ENCAP_VXLAN 0x00040000 /* VXLAN outer encapsulation */
-#define CSUM_ENCAP_RSVD1 0x00080000
+#define CSUM_ENCAP_GENEVE 0x00080000 /* GENEVE outer encapsulation */
/* Flags used to indicate that the checksum was verified by hardware. */
#define CSUM_INNER_L3_CALC 0x00100000
@@ -702,7 +702,7 @@ m_epg_pagelen(const struct mbuf *m, int pidx, int pgoff)
CSUM_INNER_IP6_TSO | CSUM_IP6_UDP | CSUM_IP6_TCP | CSUM_IP6_SCTP | \
CSUM_IP6_TSO | CSUM_IP6_ISCSI | CSUM_INNER_IP | CSUM_INNER_IP_UDP | \
CSUM_INNER_IP_TCP | CSUM_INNER_IP_TSO | CSUM_ENCAP_VXLAN | \
- CSUM_ENCAP_RSVD1 | CSUM_SND_TAG)
+ CSUM_ENCAP_GENEVE | CSUM_SND_TAG)
#define CSUM_FLAGS_RX (CSUM_INNER_L3_CALC | CSUM_INNER_L3_VALID | \
CSUM_INNER_L4_CALC | CSUM_INNER_L4_VALID | CSUM_L3_CALC | CSUM_L3_VALID | \
@@ -718,7 +718,7 @@ m_epg_pagelen(const struct mbuf *m, int pidx, int pgoff)
"\11CSUM_INNER_IP6_TSO\12CSUM_IP6_UDP\13CSUM_IP6_TCP\14CSUM_IP6_SCTP" \
"\15CSUM_IP6_TSO\16CSUM_IP6_ISCSI\17CSUM_INNER_IP\20CSUM_INNER_IP_UDP" \
"\21CSUM_INNER_IP_TCP\22CSUM_INNER_IP_TSO\23CSUM_ENCAP_VXLAN" \
- "\24CSUM_ENCAP_RSVD1\25CSUM_INNER_L3_CALC\26CSUM_INNER_L3_VALID" \
+ "\24CSUM_ENCAP_GENEVE\25CSUM_INNER_L3_CALC\26CSUM_INNER_L3_VALID" \
"\27CSUM_INNER_L4_CALC\30CSUM_INNER_L4_VALID\31CSUM_L3_CALC" \
"\32CSUM_L3_VALID\33CSUM_L4_CALC\34CSUM_L4_VALID\35CSUM_L5_CALC" \
"\36CSUM_L5_VALID\37CSUM_COALESCED\40CSUM_SND_TAG"
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
index e302d9487e64..148f2191c6e0 100644
--- a/sys/sys/priv.h
+++ b/sys/sys/priv.h
@@ -355,6 +355,7 @@
#define PRIV_NET_OVPN 422 /* Administer OpenVPN DCO. */
#define PRIV_NET_ME 423 /* Administer ME interface. */
#define PRIV_NET_WG 424 /* Administer WireGuard interface. */
+#define PRIV_NET_GENEVE 425 /* Administer geneve. */
/*
* 802.11-related privileges.
diff --git a/sys/sys/signal.h b/sys/sys/signal.h
index c0b65c0c9ef0..863b981c2b7a 100644
--- a/sys/sys/signal.h
+++ b/sys/sys/signal.h
@@ -307,6 +307,8 @@ struct __siginfo32 {
#define SEGV_ACCERR 2 /* Invalid permissions for mapped */
/* object. */
#define SEGV_PKUERR 100 /* x86: PKU violation */
+#define SEGV_MTEAERR 100 /* arm64: Asynchronous Arm MTE error */
+#define SEGV_MTESERR 101 /* arm64: Synchronous Arm MTE error */
/* codes for SIGFPE */
#define FPE_INTOVF 1 /* Integer overflow. */
diff --git a/sys/sys/time.h b/sys/sys/time.h
index 707565b6a6f1..6f18d8bd844d 100644
--- a/sys/sys/time.h
+++ b/sys/sys/time.h
@@ -355,10 +355,12 @@ tstosbt(struct timespec _ts)
static __inline sbintime_t
tstosbt_sat(struct timespec _ts)
{
+#ifndef __i386__
if (_ts.tv_sec > SBT_MAX >> 32)
return (SBT_MAX);
if (_ts.tv_sec < -(SBT_MAX >> 32) - 1)
return (-SBT_MAX - 1);
+#endif
return (tstosbt(_ts));
}
@@ -382,10 +384,12 @@ tvtosbt(struct timeval _tv)
static __inline sbintime_t
tvtosbt_sat(struct timeval _tv)
{
+#ifndef __i386__
if (_tv.tv_sec > SBT_MAX >> 32)
return (SBT_MAX);
if (_tv.tv_sec < -(SBT_MAX >> 32) - 1)
return (-SBT_MAX - 1);
+#endif
return (tvtosbt(_tv));
}
diff --git a/sys/tools/syscalls/core/scarg.lua b/sys/tools/syscalls/core/scarg.lua
index 7ffbf15b3a80..ac0332ce3a78 100644
--- a/sys/tools/syscalls/core/scarg.lua
+++ b/sys/tools/syscalls/core/scarg.lua
@@ -29,6 +29,21 @@ local function checkAbiChanges(arg)
return false
end
+-- Extracts the Microsoft(R) SAL annotations from this argument.
+local function extractArgAnnotations(arg)
+ local annotations = {}
+ for ann in arg:gmatch("_Contains_[^ ]*[_)]") do
+ table.insert(annotations, ann)
+ end
+ for ann in arg:gmatch("_In[^ ]*[_)]") do
+ table.insert(annotations, ann)
+ end
+ for ann in arg:gmatch("_Out[^ ]*[_)]") do
+ table.insert(annotations, ann)
+ end
+ return table.concat(annotations, " ")
+end
+
-- Strips the Microsoft(R) SAL annotations from this argument.
local function stripArgAnnotations(arg)
arg = arg:gsub("_Contains_[^ ]*[_)] ?", "")
@@ -46,6 +61,7 @@ function scarg:init(line)
self.arg_abi_change = checkAbiChanges(self.scarg)
self.changes_abi = self.arg_abi_change
+ self.annotation = extractArgAnnotations(self.scarg)
self.scarg = stripArgAnnotations(self.scarg)
self.name = self.scarg:match("([^* ]+)$")
@@ -126,15 +142,18 @@ function scarg:append(tbl)
table.insert(tbl, {
type = "uint32_t",
name = self.name .. "1",
+ annotation = self.annotation or "",
})
table.insert(tbl, {
type = "uint32_t",
name = self.name .. "2",
+ annotation = "",
})
else
table.insert(tbl, {
type = self.type,
name = self.name,
+ annotation = self.annotation or "",
})
end
end
diff --git a/sys/tools/syscalls/scripts/syscall_json.lua b/sys/tools/syscalls/scripts/syscall_json.lua
new file mode 100644
index 000000000000..a29510a9c4c9
--- /dev/null
+++ b/sys/tools/syscalls/scripts/syscall_json.lua
@@ -0,0 +1,126 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2026 Warner Losh <imp@bsdimp.com>
+--
+
+-- Setup to be a module, or ran as its own script.
+local syscall_json = {}
+local script = not pcall(debug.getlocal, 4, 1) -- TRUE if script.
+if script then
+ -- Add library root to the package path.
+ local path = arg[0]:gsub("/[^/]+.lua$", "")
+ package.path = package.path .. ";" .. path .. "/../?.lua"
+end
+
+local FreeBSDSyscall = require("core.freebsd-syscall")
+local ucl = require("ucl")
+
+-- Convert the type flags set (table with flag=true entries) to a sorted list.
+local function flagsToList(typetbl)
+ local flags = {}
+ for k, _ in pairs(typetbl) do
+ table.insert(flags, k)
+ end
+ table.sort(flags)
+ return flags
+end
+
+-- Convert a single syscall object to a plain table suitable for JSON export.
+-- Much of the data is available only as a method call.
+local function syscallToTable(v)
+ local entry = {
+ num = v.num,
+ name = v.name or "",
+ alias = v.alias or "",
+ audit = v.audit or "",
+ flags = flagsToList(v.type),
+ compat_level = v:compatLevel(),
+ compat_prefix = v:compatPrefix(),
+ symbol = v:symbol(),
+ rettype = v.rettype or "int",
+ cap = v.cap or "0",
+ thr = v.thr or "SY_THR_STATIC",
+ changes_abi = v.changes_abi or false,
+ noproto = v.noproto or false,
+ args_size = v.args_size or "0",
+ arg_alias = v.arg_alias or "",
+ }
+
+ -- Export arguments with annotations.
+ local args = {}
+ if v.args ~= nil then
+ for _, a in ipairs(v.args) do
+ arg = {
+ type = a.type,
+ name = a.name,
+ }
+ if a.annotation ~= nil and a.annotation ~= "" then
+ arg.annotation = a.annotation
+ end
+ table.insert(args, arg)
+ end
+ end
+ entry.args = args
+
+ -- Export altname/alttag/rettype if present (loadable syscalls).
+ if v.altname ~= nil then
+ entry.altname = v.altname
+ end
+ if v.alttag ~= nil then
+ entry.alttag = v.alttag
+ end
+
+ return entry
+end
+
+function syscall_json.generate(tbl, config)
+ -- Build the syscalls array.
+ local syscalls = {}
+ for _, v in pairs(tbl.syscalls) do
+ table.insert(syscalls, syscallToTable(v))
+ end
+
+ -- Build the structs data into a nicer structure
+ local structs = {}
+ if tbl.structs ~= nil then
+ for k, _ in pairs(tbl.structs) do
+ table.insert(structs, k)
+ end
+ table.sort(structs)
+ end
+
+ local root = {
+ syscalls = syscalls,
+ structs = structs,
+ }
+
+ local json = ucl.to_json(root)
+
+ -- Write to stdout.
+ io.write(json)
+ io.write("\n")
+end
+
+-- Entry of script:
+if script then
+ local config = require("config")
+
+ if #arg < 1 or #arg > 2 then
+ error("usage: " .. arg[0] .. " syscall.master [config]")
+ end
+
+ local sysfile, configfile = arg[1], arg[2]
+
+ config.merge(configfile)
+ config.mergeCompat()
+
+ -- The parsed system call table.
+ local tbl = FreeBSDSyscall:new{sysfile = sysfile, config = config}
+
+ syscall_json.generate(tbl, config)
+end
+
+-- Return the module.
+return syscall_json
diff --git a/sys/vm/vm_swapout.c b/sys/vm/vm_swapout.c
index f510189d24be..e85a049f46fe 100644
--- a/sys/vm/vm_swapout.c
+++ b/sys/vm/vm_swapout.c
@@ -222,13 +222,11 @@ vm_swapout_map_deactivate_pages(vm_map_t map, long desired)
{
vm_map_entry_t tmpe;
vm_object_t obj, bigobj;
- int nothingwired;
if (!vm_map_trylock_read(map))
return;
bigobj = NULL;
- nothingwired = TRUE;
/*
* first, search out the biggest object, and try to free pages from
@@ -249,8 +247,6 @@ vm_swapout_map_deactivate_pages(vm_map_t map, long desired)
VM_OBJECT_RUNLOCK(obj);
}
}
- if (tmpe->wired_count > 0)
- nothingwired = FALSE;
}
if (bigobj != NULL) {
@@ -275,15 +271,6 @@ vm_swapout_map_deactivate_pages(vm_map_t map, long desired)
}
}
- /*
- * Remove all mappings if a process is swapped out, this will free page
- * table pages.
- */
- if (desired == 0 && nothingwired) {
- pmap_remove(vm_map_pmap(map), vm_map_min(map),
- vm_map_max(map));
- }
-
vm_map_unlock_read(map);
}
diff --git a/sys/x86/acpica/acpi_apm.c b/sys/x86/acpica/acpi_apm.c
index 71e2573d5fde..e9db0c090f5c 100644
--- a/sys/x86/acpica/acpi_apm.c
+++ b/sys/x86/acpica/acpi_apm.c
@@ -430,9 +430,7 @@ apmreadfilt(struct knote *kn, long hint)
int sleeping;
clone = kn->kn_hook;
- ACPI_LOCK(acpi);
sleeping = clone->acpi_sc->acpi_next_stype != POWER_STYPE_AWAKE;
- ACPI_UNLOCK(acpi);
return (sleeping);
}
diff --git a/sys/x86/cpufreq/hwpstate_amd.c b/sys/x86/cpufreq/hwpstate_amd.c
index 4be295075482..5e075a26a57b 100644
--- a/sys/x86/cpufreq/hwpstate_amd.c
+++ b/sys/x86/cpufreq/hwpstate_amd.c
@@ -179,6 +179,7 @@ struct hwpstate_softc {
uint64_t request;
} cppc;
};
+ u_int cpuid;
};
static void hwpstate_identify(driver_t *driver, device_t parent);
@@ -337,8 +338,6 @@ static int
sysctl_cppc_dump_handler(SYSCTL_HANDLER_ARGS)
{
const struct hwpstate_softc *const sc = arg1;
- const device_t dev = sc->dev;
- const u_int cpuid = cpu_get_pcpu(dev)->pc_cpuid;
struct sbuf *sb;
struct sbuf sbs;
struct get_cppc_regs_data data;
@@ -349,16 +348,16 @@ sysctl_cppc_dump_handler(SYSCTL_HANDLER_ARGS)
sb = sbuf_new_for_sysctl(&sbs, NULL, 0, req);
- smp_rendezvous_cpu(cpuid, smp_no_rendezvous_barrier, get_cppc_regs_cb,
- smp_no_rendezvous_barrier, &data);
+ smp_rendezvous_cpu(sc->cpuid, smp_no_rendezvous_barrier,
+ get_cppc_regs_cb, smp_no_rendezvous_barrier, &data);
if (hwp_has_error(data.res, HWP_ERROR_CPPC_ENABLE))
sbuf_printf(sb, "CPU%u: " MSR_AMD_CPPC_ENABLE_NAME ": "
- MSR_NOT_READ_MSG "\n", cpuid);
+ MSR_NOT_READ_MSG "\n", sc->cpuid);
else
sbuf_printf(sb, "CPU%u: HWP %sabled (" MSR_AMD_CPPC_REQUEST_NAME
- ": %#" PRIx64 ")\n", cpuid, data.enable & 1 ? "En" : "Dis",
- data.enable);
+ ": %#" PRIx64 ")\n", sc->cpuid, data.enable & 1 ?
+ "En" : "Dis", data.enable);
if (hwp_has_error(data.res, HWP_ERROR_CPPC_CAPS))
print_cppc_no_caps_1(sb);
@@ -433,10 +432,8 @@ set_cppc_request_cb(void *args)
static inline void
set_cppc_request_send_one(struct set_cppc_request_cb *const data, device_t dev)
{
- const u_int cpuid = cpu_get_pcpu(dev)->pc_cpuid;
-
data->sc = device_get_softc(dev);
- smp_rendezvous_cpu(cpuid, smp_no_rendezvous_barrier,
+ smp_rendezvous_cpu(data->sc->cpuid, smp_no_rendezvous_barrier,
set_cppc_request_cb, smp_no_rendezvous_barrier, data);
}
@@ -505,9 +502,7 @@ sysctl_cppc_request_field_handler(SYSCTL_HANDLER_ARGS)
check_cppc_in_use(sc, __func__);
if ((sc->flags & HWPFL_CPPC_REQUEST_NOT_READ) != 0) {
- const u_int cpuid = cpu_get_pcpu(dev)->pc_cpuid;
-
- smp_rendezvous_cpu(cpuid, smp_no_rendezvous_barrier,
+ smp_rendezvous_cpu(sc->cpuid, smp_no_rendezvous_barrier,
get_cppc_request_cb, smp_no_rendezvous_barrier, sc);
if ((sc->flags & HWPFL_CPPC_REQUEST_NOT_READ) != 0)
@@ -576,8 +571,10 @@ hwpstate_goto_pstate(device_t dev, int id)
sbintime_t sbt;
uint64_t msr;
int cpu, j, limit;
+ struct hwpstate_softc *sc;
- cpu = cpu_get_pcpu(dev)->pc_cpuid;
+ sc = device_get_softc(dev);
+ cpu = sc->cpuid;
if (hwpstate_pstate_limit) {
/* get the current pstate limit */
@@ -657,16 +654,14 @@ hwpstate_set(device_t dev, const struct cf_setting *cf)
static int
hwpstate_get_cppc(device_t dev, struct cf_setting *cf)
{
- struct pcpu *pc;
+ struct hwpstate_softc *sc;
uint64_t rate;
int ret;
- pc = cpu_get_pcpu(dev);
- if (pc == NULL)
- return (ENXIO);
+ sc = device_get_softc(dev);
memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
cf->dev = dev;
- if ((ret = cpu_est_clockrate(pc->pc_cpuid, &rate)))
+ if ((ret = cpu_est_clockrate(sc->cpuid, &rate)))
return (ret);
cf->freq = rate / 1000000;
return (0);
@@ -681,7 +676,7 @@ hwpstate_get_pstate(device_t dev, struct cf_setting *cf)
int cpu;
sc = device_get_softc(dev);
- cpu = cpu_get_pcpu(dev)->pc_cpuid;
+ cpu = sc->cpuid;
hwpstate_pstate_read_status(cpu, &msr);
if (msr >= sc->cfnum)
return (EINVAL);
@@ -894,7 +889,7 @@ static int
enable_cppc(struct hwpstate_softc *sc)
{
const device_t dev = sc->dev;
- const u_int cpuid = cpu_get_pcpu(dev)->pc_cpuid;
+ const u_int cpuid = sc->cpuid;
struct set_autonomous_hwp_data data;
struct sbuf sbs;
struct sbuf *sb;
@@ -964,7 +959,7 @@ hwpstate_probe_pstate(device_t dev)
int cpu;
sc = device_get_softc(dev);
- cpu = cpu_get_pcpu(dev)->pc_cpuid;
+ cpu = sc->cpuid;
/*
* Check if acpi_perf has INFO only flag.
*/
@@ -1042,6 +1037,7 @@ hwpstate_probe(device_t dev)
device_set_desc(dev, "Cool`n'Quiet 2.0");
sc->dev = dev;
+ sc->cpuid = cpu_get_pcpu(dev)->pc_cpuid;
if ((sc->flags & HWPFL_USE_CPPC) != 0) {
sc->cpufreq_methods = &cppc_methods;
return (0);
@@ -1133,14 +1129,11 @@ static int
hwpstate_pstate_read_settings(struct hwpstate_softc *sc, uint64_t vals[])
{
struct hwpstate_pstate_read_settings_cb req;
- device_t dev;
req.sc = sc;
req.vals = vals;
- dev = sc->dev;
- smp_rendezvous_cpu(cpu_get_pcpu(dev)->pc_cpuid,
- smp_no_rendezvous_barrier, hwpstate_pstate_read_settings_cb,
- smp_no_rendezvous_barrier, &req);
+ smp_rendezvous_cpu(sc->cpuid, smp_no_rendezvous_barrier,
+ hwpstate_pstate_read_settings_cb, smp_no_rendezvous_barrier, &req);
return (req.err);
}
@@ -1155,7 +1148,7 @@ hwpstate_get_info_from_msr(device_t dev)
family = CPUID_TO_FAMILY(cpu_id);
sc = device_get_softc(dev);
/* Get pstate count */
- hwpstate_pstate_read_limit(cpu_get_pcpu(dev)->pc_cpuid, &msr);
+ hwpstate_pstate_read_limit(sc->cpuid, &msr);
sc->cfnum = 1 + AMD_10H_11H_GET_PSTATE_MAX_VAL(msr);
hwpstate_set = sc->hwpstate_settings;
hwpstate_pstate_read_settings(sc, state_settings);
diff --git a/sys/x86/include/cputypes.h b/sys/x86/include/cputypes.h
index f2bd3c2983f0..0b6f0f3746a9 100644
--- a/sys/x86/include/cputypes.h
+++ b/sys/x86/include/cputypes.h
@@ -45,4 +45,18 @@
#define CPU_VENDOR_CENTAUR CPU_VENDOR_IDT
#define CPU_VENDOR_HYGON 0x1d94 /* Hygon */
+#define CPU_AMD_ZEN1 0x00
+#define CPU_AMD_ZEN2 0x01
+#define CPU_AMD_ZEN3 0x02
+#define CPU_AMD_ZEN4 0x03
+#define CPU_AMD_ZEN5 0x04
+#define CPU_AMD_ZEN6 0x05
+#define CPU_AMD_UNKNOWN 0xffffffff
+
+#ifdef _KERNEL
+#ifndef LOCORE
+u_int ident_zen_cpu(void);
+#endif
+#endif
+
#endif /* !_X86_CPUTYPES_H_ */
diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c
index 7e0ccc72f7bb..75144ccae8bc 100644
--- a/sys/x86/x86/identcpu.c
+++ b/sys/x86/x86/identcpu.c
@@ -64,6 +64,7 @@
#include <machine/specialreg.h>
#include <amd64/vmm/intel/vmx_controls.h>
+#include <x86/cputypes.h>
#include <x86/isa/icu.h>
#include <x86/vmware.h>
@@ -2690,3 +2691,49 @@ cpu_getmaxphyaddr(void)
#endif
return ((1ULL << cpu_maxphyaddr) - 1);
}
+
+const static struct {
+ u_int family;
+ u_int model_min;
+ u_int model_max;
+ u_int generation;
+} zen_idents[] = {
+ { .family = 0x17, .model_min = 0x00, .model_max = 0x2f, .generation = CPU_AMD_ZEN1 },
+ { .family = 0x17, .model_min = 0x50, .model_max = 0x5f, .generation = CPU_AMD_ZEN1 },
+ { .family = 0x17, .model_min = 0x30, .model_max = 0x4f, .generation = CPU_AMD_ZEN2 },
+ { .family = 0x17, .model_min = 0x60, .model_max = 0x7f, .generation = CPU_AMD_ZEN2 },
+ { .family = 0x17, .model_min = 0x90, .model_max = 0x91, .generation = CPU_AMD_ZEN2 },
+ { .family = 0x17, .model_min = 0xa0, .model_max = 0xaf, .generation = CPU_AMD_ZEN2 },
+ { .family = 0x19, .model_min = 0x00, .model_max = 0x0f, .generation = CPU_AMD_ZEN3 },
+ { .family = 0x19, .model_min = 0x20, .model_max = 0x5f, .generation = CPU_AMD_ZEN3 },
+ { .family = 0x19, .model_min = 0x10, .model_max = 0x1f, .generation = CPU_AMD_ZEN4 },
+ { .family = 0x19, .model_min = 0x60, .model_max = 0xaf, .generation = CPU_AMD_ZEN4 },
+ { .family = 0x1a, .model_min = 0x00, .model_max = 0x2f, .generation = CPU_AMD_ZEN5 },
+ { .family = 0x1a, .model_min = 0x40, .model_max = 0x4f, .generation = CPU_AMD_ZEN5 },
+ { .family = 0x1a, .model_min = 0x60, .model_max = 0x7f, .generation = CPU_AMD_ZEN5 },
+ { .family = 0x1a, .model_min = 0x50, .model_max = 0x5f, .generation = CPU_AMD_ZEN6 },
+ { .family = 0x1a, .model_min = 0x80, .model_max = 0xaf, .generation = CPU_AMD_ZEN6 },
+ { .family = 0x1a, .model_min = 0xc0, .model_max = 0xcf, .generation = CPU_AMD_ZEN6 },
+};
+
+u_int
+ident_zen_cpu(void)
+{
+ u_int family = CPUID_TO_FAMILY(cpu_id);
+ u_int model = CPUID_TO_MODEL(cpu_id);
+ int i;
+
+ if (cpu_vendor_id != CPU_VENDOR_AMD)
+ return (CPU_AMD_UNKNOWN);
+
+ for (i = 0; i < nitems(zen_idents); i++) {
+ if (family != zen_idents[i].family)
+ continue;
+ if (model < zen_idents[i].model_min ||
+ model > zen_idents[i].model_max)
+ continue;
+ return (zen_idents[i].generation);
+ }
+
+ return (CPU_AMD_UNKNOWN);
+}
diff --git a/tests/ci/tools/ci.conf b/tests/ci/tools/ci.conf
index 2302fc479a47..5ddb3476eb26 100644
--- a/tests/ci/tools/ci.conf
+++ b/tests/ci/tools/ci.conf
@@ -74,6 +74,7 @@ kld_list="\${kld_list} fusefs" # sys/fs/fusefs
kld_list="\${kld_list} if_bridge" # sys/net/if_bridge_test
kld_list="\${kld_list} if_enc" # sys/netpfil/pf
kld_list="\${kld_list} if_epair" # sys/net/if_epair_test
+kld_list="\${kld_list} if_geneve" # sys/net/if_geneve
kld_list="\${kld_list} if_ovpn" # sys/net/if_ovpn
kld_list="\${kld_list} if_stf" # sys/net/if_stf
kld_list="\${kld_list} ipdivert" # sys/netinet (loads ipdivert)
diff --git a/tests/sys/arch/Makefile b/tests/sys/arch/Makefile
index e1a35422410e..40edad27507d 100644
--- a/tests/sys/arch/Makefile
+++ b/tests/sys/arch/Makefile
@@ -1,5 +1,7 @@
+TESTSDIR= ${TESTSBASE}/sys/arch
+
.if exists(${.CURDIR}/${MACHINE_ARCH})
-SUBDIR+= ${MACHINE_ARCH}
+TESTS_SUBDIRS+= ${MACHINE_ARCH}
.endif
-.include <bsd.subdir.mk>
+.include <bsd.test.mk>
diff --git a/tests/sys/arch/Makefile.inc b/tests/sys/arch/Makefile.inc
index cf5c687d6401..01b5f23410c8 100644
--- a/tests/sys/arch/Makefile.inc
+++ b/tests/sys/arch/Makefile.inc
@@ -1,3 +1 @@
-TESTSDIR= ${TESTSBASE}/sys/arch
-
.include "../Makefile.inc"
diff --git a/tests/sys/cam/ctl/Makefile b/tests/sys/cam/ctl/Makefile
index 05f0831fc8b0..4707a2f4b357 100644
--- a/tests/sys/cam/ctl/Makefile
+++ b/tests/sys/cam/ctl/Makefile
@@ -3,8 +3,10 @@ PACKAGE= tests
TESTSDIR= ${TESTSBASE}/sys/cam/ctl
BINDIR=${TESTSDIR}
+${PACKAGE}FILES+= all-supported-opcodes.txt
${PACKAGE}FILES+= ctl.subr
+ATF_TESTS_SH+= opcodes
ATF_TESTS_SH+= persist
ATF_TESTS_SH+= prevent
ATF_TESTS_SH+= read_buffer
diff --git a/tests/sys/cam/ctl/all-supported-opcodes.txt b/tests/sys/cam/ctl/all-supported-opcodes.txt
new file mode 100644
index 000000000000..d1725b5a7c80
--- /dev/null
+++ b/tests/sys/cam/ctl/all-supported-opcodes.txt
@@ -0,0 +1,39 @@
+ 00 00 00 02 60 00 00 00 00 00 00 00 06 03 00 00 00
+ 10 00 00 00 06 04 00 00 00 00 00 00 06 08 00 00 00
+ 20 00 00 00 06 0a 00 00 00 00 00 00 06 12 00 00 00
+ 30 00 00 00 06 15 00 00 00 00 00 00 06 16 00 00 00
+ 40 00 00 00 06 17 00 00 00 00 00 00 06 1a 00 00 00
+ 50 00 00 00 06 1b 00 00 00 00 00 00 06 1e 00 00 00
+ 60 00 00 00 06 25 00 00 00 00 00 00 0a 28 00 00 00
+ 70 00 00 00 0a 2a 00 00 00 00 00 00 0a 2e 00 00 00
+ 80 00 00 00 0a 2f 00 00 00 00 00 00 0a 35 00 00 00
+ 90 00 00 00 0a 37 00 00 00 00 00 00 0a 3b 00 00 02
+ a0 00 01 00 0a 3c 00 00 02 00 01 00 0a 3c 00 00 03
+ b0 00 01 00 0a 3c 00 00 0b 00 01 00 0a 41 00 00 00
+ c0 00 00 00 0a 42 00 00 00 00 00 00 0a 4d 00 00 00
+ d0 00 00 00 0a 55 00 00 00 00 00 00 0a 56 00 00 00
+ e0 00 00 00 0a 57 00 00 00 00 00 00 0a 5a 00 00 00
+ f0 00 00 00 0a 5e 00 00 00 00 01 00 0a 5e 00 00 01
+ 100 00 01 00 0a 5e 00 00 02 00 01 00 0a 5e 00 00 03
+ 110 00 01 00 0a 5f 00 00 00 00 01 00 0a 5f 00 00 01
+ 120 00 01 00 0a 5f 00 00 02 00 01 00 0a 5f 00 00 03
+ 130 00 01 00 0a 5f 00 00 04 00 01 00 0a 5f 00 00 05
+ 140 00 01 00 0a 5f 00 00 06 00 01 00 0a 83 00 00 00
+ 150 00 01 00 10 83 00 00 01 00 01 00 10 83 00 00 10
+ 160 00 01 00 10 83 00 00 11 00 01 00 10 83 00 00 1c
+ 170 00 01 00 10 84 00 00 00 00 01 00 10 84 00 00 03
+ 180 00 01 00 10 84 00 00 04 00 01 00 10 84 00 00 05
+ 190 00 01 00 10 84 00 00 07 00 01 00 10 84 00 00 08
+ 1a0 00 01 00 10 88 00 00 00 00 00 00 10 89 00 00 00
+ 1b0 00 00 00 10 8a 00 00 00 00 00 00 10 8e 00 00 00
+ 1c0 00 00 00 10 8f 00 00 00 00 00 00 10 91 00 00 00
+ 1d0 00 00 00 10 93 00 00 00 00 00 00 10 9b 00 00 02
+ 1e0 00 01 00 10 9b 00 00 03 00 01 00 10 9b 00 00 0b
+ 1f0 00 01 00 10 9c 00 00 00 00 00 00 10 9e 00 00 10
+ 200 00 01 00 10 9e 00 00 12 00 01 00 10 a0 00 00 00
+ 210 00 00 00 0c a3 00 00 05 00 01 00 0c a3 00 00 0a
+ 220 00 01 00 0c a3 00 00 0c 00 01 00 0c a3 00 00 0d
+ 230 00 01 00 0c a3 00 00 0f 00 01 00 0c a8 00 00 00
+ 240 00 00 00 0c aa 00 00 00 00 00 00 0c ae 00 00 00
+ 250 00 00 00 0c af 00 00 00 00 00 00 0c b7 00 00 00
+ 260 00 00 00 0c
diff --git a/tests/sys/cam/ctl/opcodes.sh b/tests/sys/cam/ctl/opcodes.sh
new file mode 100644
index 000000000000..1d558a1095a0
--- /dev/null
+++ b/tests/sys/cam/ctl/opcodes.sh
@@ -0,0 +1,241 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Axcient
+# 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 DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Not Tested
+# * Allocation length, because sg3_utils 1.48 does not provide a way to set it.
+# * RCTD bit, because CTL does not support it.
+
+. $(atf_get_srcdir)/ctl.subr
+
+require_sg_opcodes_version()
+{
+ WANT=$1
+ HAVE=$(sg_opcodes -V 2>&1 | cut -w -f 3)
+ if [ `echo "$HAVE >= $WANT" | bc -l` = 0 ]; then
+ atf_skip "This test requires sg_opcodes $WANT or greater"
+ fi
+}
+
+# Query all supported opcodes.
+# NB: the fixture here may need to change frequently, any time CTL gains
+# support for new opcodes or service actions.
+atf_test_case all_opcodes cleanup
+all_opcodes_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES can report all supported opcodes"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+all_opcodes_body()
+{
+ create_ramdisk
+
+ atf_check -o file:$(atf_get_srcdir)/all-supported-opcodes.txt sg_opcodes -p disk -nH /dev/$dev
+}
+all_opcodes_cleanup()
+{
+ cleanup
+}
+
+# Query support for a single opcode. The REPORTING OPTIONS field will be 1 and
+# REQUESTED SERVICE ACTION will be zero.
+atf_test_case basic cleanup
+basic_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES can report a single supported opcode"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+basic_body()
+{
+ create_ramdisk
+
+ atf_check -o inline:" 00 00 03 00 0a 28 1a ff ff ff ff 00 ff ff 07\n" sg_opcodes -o 0x28 -p disk -nH /dev/$dev
+}
+basic_cleanup()
+{
+ cleanup
+}
+
+atf_test_case invalid_rep_opts cleanup
+invalid_rep_opts_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES will fail gracefully if the REPORTING OPTIONS field is set to an invalid value"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+invalid_rep_opts_body()
+{
+ require_sg_opcodes_version 1.03
+ create_ramdisk
+
+ atf_check -o ignore -e ignore -s exit:5 sg_opcodes -o 0x28 -p disk -n --rep-opts=4 /dev/$dev
+}
+invalid_rep_opts_cleanup()
+{
+ cleanup
+}
+
+# Try to query support for an opcode that needs a service action, but without
+# specifying a service action.
+atf_test_case missing_service_action cleanup
+missing_service_action_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES fails gracefully if the service action is omitted"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+missing_service_action_body()
+{
+ create_ramdisk
+
+ atf_check -e ignore -s exit:5 sg_opcodes -o 0x3c -p disk -n /dev/$dev
+}
+missing_service_action_cleanup()
+{
+ cleanup
+}
+
+# Regression test for CVE-2024-42416
+atf_test_case out_of_bounds_service_action cleanup
+out_of_bounds_service_action_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES fails gracefully if the requested service action is out of bounds"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+out_of_bounds_service_action_body()
+{
+ require_sg_opcodes_version 1.03
+ create_ramdisk
+
+ # opcode 0 (Test Unit Ready) does not take service actions
+ # opcode 0x3c (Read Buffer(10)) does take service actions
+ for opcode in 0 0x3c; do
+ for ro in 2 3; do
+ for sa in 32 100 255 256 10000 65535; do
+ atf_check -s exit:5 -o ignore -e ignore sg_opcodes --rep-opts=$ro -o $opcode -s $sa -p disk -nH /dev/$dev
+ done
+ done
+ done
+}
+out_of_bounds_service_action_cleanup()
+{
+ cleanup
+}
+
+# Query support for an opcode that needs a service action
+atf_test_case service_action cleanup
+service_action_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES can query an opcode that needs a service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+service_action_body()
+{
+ create_ramdisk
+
+ atf_check -o inline:" 00 00 03 00 0a 3c 02 00 ff ff ff ff ff ff 07\n" sg_opcodes -o 0x3c -s 2 -p disk -nH /dev/$dev
+}
+service_action_cleanup()
+{
+ cleanup
+}
+
+# Try to query support for an opcode that does not need a service action, but
+# provide one anyway.
+atf_test_case unexpected_service_action cleanup
+unexpected_service_action_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES fails gracefully if an extraneous service action is provided"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+unexpected_service_action_body()
+{
+ create_ramdisk
+
+ atf_check -e ignore -s exit:5 sg_opcodes -o 0x28 -s 1 -p disk -n /dev/$dev
+}
+unexpected_service_action_cleanup()
+{
+ cleanup
+}
+
+# Try to query support for an opcode that does not need a service action, but
+# provide one anyway. Set REPORTING OPTIONS to 3. This requests that the
+# command be reported as unsupported, but REQUEST SUPPORTED OPCODES will return
+# successfully.
+atf_test_case unexpected_service_action_ro3 cleanup
+unexpected_service_action_ro3_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES fails gracefully if an extraneous service action is provided, using REPORTING OPTIONS 3"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+unexpected_service_action_ro3_body()
+{
+ require_sg_opcodes_version 1.03
+ create_ramdisk
+
+ atf_check -e ignore -o inline:" 00 00 01 00 00\n" sg_opcodes --rep-opts=3 -o 0xb7 -s 1 -p disk -nH /dev/$dev
+}
+unexpected_service_action_ro3_cleanup()
+{
+ cleanup
+}
+
+atf_test_case unsupported_opcode cleanup
+unsupported_opcode_head()
+{
+ atf_set "descr" "REPORT SUPPORTED OPCODES can report a single unsupported opcode"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_opcodes
+}
+unsupported_opcode_body()
+{
+ create_ramdisk
+
+ atf_check -o inline:" 00 00 01 00 00\n" sg_opcodes -o 1 -p disk -nH /dev/$dev
+}
+unsupported_opcode_cleanup()
+{
+ cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case all_opcodes
+ atf_add_test_case basic
+ atf_add_test_case invalid_rep_opts
+ atf_add_test_case missing_service_action
+ atf_add_test_case out_of_bounds_service_action
+ atf_add_test_case service_action
+ atf_add_test_case unsupported_opcode
+ atf_add_test_case unexpected_service_action
+ atf_add_test_case unexpected_service_action_ro3
+}
diff --git a/tests/sys/fs/fusefs/rename.cc b/tests/sys/fs/fusefs/rename.cc
index 385f26953aae..d518071ca681 100644
--- a/tests/sys/fs/fusefs/rename.cc
+++ b/tests/sys/fs/fusefs/rename.cc
@@ -239,8 +239,8 @@ TEST_F(Rename, erelookup)
_)
).WillRepeatedly(Invoke(ReturnErrno(EIO)));
- ASSERT_EQ(0, pthread_create(&th0, NULL, setattr_th, (void*)FULLSRC))
- << strerror(errno);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, setattr_th,
+ const_cast<char *>(FULLSRC))) << strerror(errno);
ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &timeout));
timeout.tv_nsec += NAP_NS;
diff --git a/tests/sys/kqueue/kqueue_fork.c b/tests/sys/kqueue/kqueue_fork.c
index 6f517a2e0e29..ad8f69056e07 100644
--- a/tests/sys/kqueue/kqueue_fork.c
+++ b/tests/sys/kqueue/kqueue_fork.c
@@ -92,21 +92,68 @@ ATF_TC_BODY(shared_table_filt_sig, tc)
#define RECV_VNODE 0x02
#define RECV_CLOREAD 0x04
#define RECV_ERROR 0x80
-#define RECV_ALL (RECV_TIMER | RECV_VNODE)
+
+static const struct cponfork_recv {
+ const char *recv_error_desc;
+ unsigned int recv_bit;
+ bool recv_parent_only;
+} cponfork_recv[] = {
+ { "EVFILT_TIMER did not fire", RECV_TIMER, false },
+ { "EVFILT_VNODE expected with creation of canary", RECV_VNODE, false },
+ { "EVFILT_READ received for fd closed on fork", RECV_CLOREAD, true },
+};
+
+static void
+cponfork_notes_mask_check(unsigned int mask, bool childmask)
+{
+ const struct cponfork_recv *rcv;
+ unsigned int expect;
+
+ ATF_REQUIRE(mask != RECV_ERROR);
+ for (size_t i = 0; i < nitems(cponfork_recv); i++) {
+ rcv = &cponfork_recv[i];
+
+ expect = childmask && rcv->recv_parent_only ? 0 : rcv->recv_bit;
+ ATF_REQUIRE_EQ_MSG(expect, mask & rcv->recv_bit,
+ "%s (%s, mask %x)", rcv->recv_error_desc,
+ childmask ? "child" : "parent",
+ mask);
+ }
+}
+
+static unsigned int
+cponfork_notes_mask(bool inchild)
+{
+ const struct cponfork_recv *rcv;
+ unsigned int mask = 0;
+
+ for (size_t i = 0; i < nitems(cponfork_recv); i++) {
+ rcv = &cponfork_recv[i];
+
+ if (!inchild || !rcv->recv_parent_only)
+ mask |= rcv->recv_bit;
+ }
+
+ ATF_REQUIRE(mask != 0);
+ return (mask);
+}
static int
cponfork_notes_check(int kq, int clofd)
{
struct kevent ev;
+ unsigned int mask;
int error, received = 0;
+ mask = cponfork_notes_mask(true);
+
EV_SET(&ev, TIMER_TIMEOUT, EVFILT_TIMER,
EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_SECONDS, 4, NULL);
error = kevent(kq, &ev, 1, NULL, 0, NULL);
if (error == -1)
return (RECV_ERROR);
- while ((received & RECV_ALL) != RECV_ALL) {
+ while ((received & mask) != mask) {
error = kevent(kq, NULL, 0, &ev, 1, NULL);
if (error < 0)
return (RECV_ERROR);
@@ -138,7 +185,8 @@ ATF_TC_WITHOUT_HEAD(cponfork_notes);
ATF_TC_BODY(cponfork_notes, tc)
{
struct kevent ev[3];
- int clofd, dfd, error, kq, pdfd, pmask, status;
+ siginfo_t info;
+ int clofd, dfd, error, kq, pdfd, pmask;
pid_t pid;
kq = kqueuex(KQUEUE_CPONFORK);
@@ -164,8 +212,8 @@ ATF_TC_BODY(cponfork_notes, tc)
/*
* Every event we setup here we should expect to observe in both the
* child and the parent, with exception to the EVFILT_READ of clofd. We
- * except that one to be dropped in the child when the kqueue it's
- * attached to goes away, thus its exclusion from the RECV_ALL mask.
+ * expect that one to be dropped in the child when the kqueue it's
+ * attached to goes away, thus its exclusion from the child mask.
*/
EV_SET(&ev[0], dfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_WRITE, 0, NULL);
@@ -195,6 +243,9 @@ ATF_TC_BODY(cponfork_notes, tc)
else if (kf.kf_type != KF_TYPE_KQUEUE)
_exit(RECV_ERROR);
+ if (fcntl(clofd, F_KINFO, &kf) != -1 || errno != EBADF)
+ _exit(RECV_ERROR);
+
_exit(cponfork_notes_check(kq, clofd));
}
@@ -209,15 +260,13 @@ ATF_TC_BODY(cponfork_notes, tc)
* still fire twice (once in parent, once in child).
*/
pmask = cponfork_notes_check(kq, clofd);
- ATF_REQUIRE_EQ(pmask, RECV_ALL | RECV_CLOREAD);
+ cponfork_notes_mask_check(pmask, false);
/* Wait for the child to timeout or observe the timer. */
- _Static_assert(RECV_ALL <= UCHAR_MAX,
- "Too many events to observe -- switch from waitpid -> waitid");
- error = waitpid(pid, &status, 0);
+ error = waitid(P_PID, pid, &info, WEXITED);
ATF_REQUIRE(error != -1);
- ATF_REQUIRE(WIFEXITED(status));
- ATF_REQUIRE_EQ(WEXITSTATUS(status), RECV_ALL);
+ ATF_REQUIRE_EQ(CLD_EXITED, info.si_code);
+ cponfork_notes_mask_check(info.si_status, true);
}
ATF_TP_ADD_TCS(tp)
diff --git a/tests/sys/net/Makefile b/tests/sys/net/Makefile
index e390c6e8059d..6dcc23b49b67 100644
--- a/tests/sys/net/Makefile
+++ b/tests/sys/net/Makefile
@@ -15,6 +15,7 @@ ATF_TESTS_SH+= if_stf
ATF_TESTS_SH+= if_tun_test
ATF_TESTS_SH+= if_vlan
ATF_TESTS_SH+= if_wg
+ATF_TESTS_SH+= if_geneve
TESTS_SUBDIRS+= bpf
TESTS_SUBDIRS+= if_ovpn
diff --git a/tests/sys/net/if_geneve.sh b/tests/sys/net/if_geneve.sh
new file mode 100644
index 000000000000..7eb2649b44a9
--- /dev/null
+++ b/tests/sys/net/if_geneve.sh
@@ -0,0 +1,1000 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025-2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "ether_ipv4" "cleanup"
+ether_ipv4_head()
+{
+ atf_set descr 'Create a geneve(4) l2 tunnel over an ipv4 underlay using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv4_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+}
+
+ether_ipv4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv6" "cleanup"
+ether_ipv6_head()
+{
+ atf_set descr 'Create a geneve(4) l2 tunnel over an ipv6 underlay using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv6_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+}
+
+ether_ipv6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_ipv4" "cleanup"
+inherit_ipv4_head()
+{
+ atf_set descr 'Create a geneve(4) l3 tunnel over an ipv4 underlay using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv4_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=2
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+}
+
+inherit_ipv4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_ipv6" "cleanup"
+inherit_ipv6_head()
+{
+ atf_set descr 'Create a geneve(4) l3 tunnel over an ipv6 underlay using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv6_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+}
+
+inherit_ipv6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv6_blind_options" "cleanup"
+ether_ipv6_blind_options_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv6 tunnel and test geneve options'
+ atf_set require.user root
+}
+
+ether_ipv6_blind_options_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevemaxaddr 1000
+ atf_check -s exit:0 -o match:"max: 1000" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevetimeout 1000
+ atf_check -s exit:0 -o match:"timeout: 1000" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 -genevelearn
+ atf_check -s exit:0 -o match:"mode: nolearning" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevelearn
+ atf_check -s exit:0 -o match:" learning" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o match:"count: 1" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 geneveflush
+ atf_check -s exit:0 -o match:"count: 0" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 geneveflushall
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevettl inherit
+ atf_check -s exit:0 -o match:"ttl: inherit" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevettl 1
+ atf_check -s exit:0 -o match:"ttl: 1" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 down genevedf set up
+ atf_check -s exit:0 -o match:"df: set" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 down genevedf inherit up
+ atf_check -s exit:0 -o match:"df: inherit" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 down genevedf unset up
+ atf_check -s exit:0 -o match:"df: unset" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 genevedscpinherit
+ atf_check -s exit:0 -o match:"dscp: inherit" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 -genevedscpinherit
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 geneveexternal
+ atf_check -s exit:0 -o match:" external" ifconfig -j genevetest1 -v geneve1
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 -geneveexternal
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 down geneveportrange 11000 62000 up
+ atf_check -s exit:0 -o match:"portrange: 11000-62000" ifconfig -j genevetest1 -v geneve1
+
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+}
+
+ether_ipv6_blind_options_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv6_external" "cleanup"
+ether_ipv6_external_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv6 tunnel and test geneve collect metadata'
+ atf_set require.user root
+}
+
+ether_ipv6_external_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2
+ local v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint2} genevelocal ${endpoint1} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 geneveremote ${endpoint1} genevelocal ${endpoint2} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 geneveexternal
+ atf_check -s exit:16 -e ignore ifconfig -j genevetest1 geneve1 down geneveid 10 up
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 -geneveexternal
+ atf_check -s exit:0 -o ignore ifconfig -j genevetest1 geneve1 down geneveid 10 up
+
+}
+
+ether_ipv6_external_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv4_multicast" "cleanup"
+ether_ipv4_multicast_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv4 multicast tunnel using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv4_multicast_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ mc_group=239.0.0.1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip_mroute; then
+ atf_skip "This test requires ip_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 add -net 239.0.0.0/8 -interface ${epair}a
+ route -j genevetest2 add -net 239.0.0.0/8 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint1} \
+ genevegroup ${mc_group} genevedev ${epair}a up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint2} \
+ genevegroup ${mc_group} genevedev ${epair}b up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest1 ifmcstat -i ${epair}a -f inet
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest2 ifmcstat -i ${epair}b -f inet
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+ether_ipv4_multicast_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv6_multicast" "cleanup"
+ether_ipv6_multicast_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv6 multicast tunnel using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv6_multicast_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ mc_group=ff08::db8:0:1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip6_mroute; then
+ atf_skip "This test requires ip6_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 -n6 add -net ff08::db8:0:1/96 -interface ${epair}a
+ route -j genevetest2 -n6 add -net ff08::db8:0:1/96 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint1} \
+ genevegroup ${mc_group} genevedev ${epair}a up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint2} \
+ genevegroup ${mc_group} genevedev ${epair}b up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+ether_ipv6_multicast_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "ether_ipv4_multicast_without_dev" "cleanup"
+ether_ipv4_multicast_without_dev_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv4 multicast tunnel without specifying genevedev using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv4_multicast_without_dev_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ mc_group=239.0.0.1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip_mroute; then
+ atf_skip "This test requires ip_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 add -net 239.0.0.0/8 -interface ${epair}a
+ route -j genevetest2 add -net 239.0.0.0/8 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint1} genevegroup ${mc_group} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint2} genevegroup ${mc_group} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest1 ifmcstat -i ${epair}a -f inet
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest2 ifmcstat -i ${epair}b -f inet
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+ether_ipv4_multicast_without_dev_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "ether_ipv6_multicast_without_dev" "cleanup"
+ether_ipv6_multicast_without_dev_head()
+{
+ atf_set descr 'Create a geneve(4) l2 ipv6 multicast tunnel without specifying genevedev using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+ether_ipv6_multicast_without_dev_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ mc_group=ff08::db8:0:1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip6_mroute; then
+ atf_skip "This test requires ip6_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 -n6 add -net ff08::db8:0:1/96 -interface ${epair}a
+ route -j genevetest2 -n6 add -net ff08::db8:0:1/96 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint1} genevegroup ${mc_group} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l2 debug \
+ geneveid $vni1 genevelocal ${endpoint2} genevegroup ${mc_group} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/24
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+ether_ipv6_multicast_without_dev_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_ipv4_multicast" "cleanup"
+inherit_ipv4_multicast_head()
+{
+ atf_set descr 'Create a geneve(4) l3 ipv4 multicast tunnel using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv4_multicast_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ mc_group=239.0.0.1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip_mroute; then
+ atf_skip "This test requires ip_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 add -net 239.0.0.0/8 -interface ${epair}a
+ route -j genevetest2 add -net 239.0.0.0/8 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint1} \
+ genevegroup ${mc_group} genevedev ${epair}a up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint2} \
+ genevegroup ${mc_group} genevedev ${epair}b up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore sysctl -j genevetest1 net.inet.icmp.bmcastecho=1
+ atf_check -s exit:0 -o ignore sysctl -j genevetest2 net.inet.icmp.bmcastecho=1
+
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest1 ifmcstat -i ${epair}a -f inet
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest2 ifmcstat -i ${epair}b -f inet
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+inherit_ipv4_multicast_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_ipv6_multicast" "cleanup"
+inherit_ipv6_multicast_head()
+{
+ atf_set descr 'Create a geneve(4) l3 ipv6 multicast tunnel using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv6_multicast_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ mc_group=ff08::db8:0:1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip6_mroute; then
+ atf_skip "This test requires ip6_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 -n6 add -net ff08::db8:0:1/96 -interface ${epair}a
+ route -j genevetest2 -n6 add -net ff08::db8:0:1/96 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint1} \
+ genevegroup ${mc_group} genevedev ${epair}a up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint2} \
+ genevegroup ${mc_group} genevedev ${epair}b up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore sysctl -j genevetest1 net.inet.icmp.bmcastecho=1
+ atf_check -s exit:0 -o ignore sysctl -j genevetest2 net.inet.icmp.bmcastecho=1
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+
+}
+
+inherit_ipv6_multicast_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_ipv4_multicast_without_dev" "cleanup"
+inherit_ipv4_multicast_without_dev_head()
+{
+ atf_set descr 'Create a geneve(4) l3 ipv4 multicast tunnel without specifying genevedev using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv4_multicast_without_dev_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ mc_group=239.0.0.1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip_mroute; then
+ atf_skip "This test requires ip_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet ${endpoint1}/24 up
+ ifconfig -j genevetest2 ${epair}b inet ${endpoint2}/24 up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 add -net 239.0.0.0/8 -interface ${epair}a
+ route -j genevetest2 add -net 239.0.0.0/8 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint1} genevegroup ${mc_group} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint2} genevegroup ${mc_group} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore sysctl -j genevetest1 net.inet.icmp.bmcastecho=1
+ atf_check -s exit:0 -o ignore sysctl -j genevetest2 net.inet.icmp.bmcastecho=1
+
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest1 ifmcstat -i ${epair}a -f inet
+ atf_check -s exit:0 -o match:"group 239.0.0.1" jexec genevetest2 ifmcstat -i ${epair}b -f inet
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+
+}
+
+inherit_ipv4_multicast_without_dev_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "inherit_ipv6_multicast_without_dev" "cleanup"
+inherit_ipv6_multicast_without_dev_head()
+{
+ atf_set descr 'Create a geneve(4) l3 ipv6 multicast tunnel without specifying genevedev using epair and pass traffic between jails'
+ atf_set require.user root
+}
+
+inherit_ipv6_multicast_without_dev_body()
+{
+ local epair geneve1 geneve2 vni1 endpoint1 endpoint2 mc_group
+ local v4tunnel1 v4tunnel2 v6tunnel1 v6tunnel2
+
+ endpoint1=3fff::1
+ endpoint2=3fff::2
+ mc_group=ff08::db8:0:1
+ v4tunnel1=169.254.0.1
+ v4tunnel2=169.254.0.2
+ v6tunnel1=2001:db8::1
+ v6tunnel2=2001:db8::2
+ vni1=1
+
+ if ! kldstat -q -m if_geneve; then
+ atf_skip "This test requires if_geneve"
+ fi
+ if ! kldstat -q -m ip6_mroute; then
+ atf_skip "This test requires ip6_mroute"
+ fi
+
+ vnet_init
+ epair=$(vnet_mkepair)
+ vnet_mkjail genevetest1 ${epair}a
+ vnet_mkjail genevetest2 ${epair}b
+
+ ifconfig -j genevetest1 ${epair}a inet6 ${endpoint1} up
+ ifconfig -j genevetest2 ${epair}b inet6 ${endpoint2} up
+
+ # manually add the multicast routes to epairs
+ route -j genevetest1 -n6 add -net ff08::db8:0:1/96 -interface ${epair}a
+ route -j genevetest2 -n6 add -net ff08::db8:0:1/96 -interface ${epair}b
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint1} genevegroup ${mc_group} up
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 create genevemode l3 debug \
+ geneveid $vni1 genevelocal ${endpoint2} genevegroup ${mc_group} up
+
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet ${v4tunnel1}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest1 geneve1 inet6 ${v6tunnel1}
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet ${v4tunnel2}/30
+ atf_check -s exit:0 -o ignore \
+ ifconfig -j genevetest2 geneve1 inet6 ${v6tunnel2}
+
+ atf_check -s exit:0 -o ignore sysctl -j genevetest1 net.inet.icmp.bmcastecho=1
+ atf_check -s exit:0 -o ignore sysctl -j genevetest2 net.inet.icmp.bmcastecho=1
+
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v6tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v6tunnel1
+ atf_check -s exit:0 -o ignore jexec genevetest1 ping -nc 1 -t 1 $v4tunnel2
+ atf_check -s exit:0 -o ignore jexec genevetest2 ping -nc 1 -t 1 $v4tunnel1
+
+}
+
+inherit_ipv6_multicast_without_dev_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ether_ipv4"
+ atf_add_test_case "ether_ipv4_multicast"
+ atf_add_test_case "ether_ipv4_multicast_without_dev"
+ atf_add_test_case "ether_ipv6"
+ atf_add_test_case "ether_ipv6_blind_options"
+ atf_add_test_case "ether_ipv6_external"
+ atf_add_test_case "ether_ipv6_multicast"
+ atf_add_test_case "ether_ipv6_multicast_without_dev"
+ atf_add_test_case "inherit_ipv4"
+ atf_add_test_case "inherit_ipv4_multicast"
+ atf_add_test_case "inherit_ipv4_multicast_without_dev"
+ atf_add_test_case "inherit_ipv6"
+ atf_add_test_case "inherit_ipv6_multicast"
+ atf_add_test_case "inherit_ipv6_multicast_without_dev"
+}
diff --git a/tests/sys/netinet/ip_mroute.py b/tests/sys/netinet/ip_mroute.py
index 5416d824d3c2..c79b046445e5 100644
--- a/tests/sys/netinet/ip_mroute.py
+++ b/tests/sys/netinet/ip_mroute.py
@@ -21,7 +21,9 @@ class MRouteTestTemplate(VnetTestTemplate):
COORD_SOCK = "coord.sock"
@staticmethod
- def _msgwait(sock: socket.socket, expected: bytes):
+ def _msgwait(sock: socket.socket, expected: bytes, timeout=None):
+ if timeout is not None:
+ sock.settimeout(timeout)
msg = sock.recv(1024)
assert msg == expected
@@ -196,12 +198,7 @@ class Test1RBasicINET(MRouteINETTestTemplate):
self.waittest()
-class Test1RCrissCrossINET(MRouteINETTestTemplate):
- """
- Test a router connected to four hosts, with pairs of interfaces
- in different FIBs.
- """
-
+class MRouteINETCrissCrossTestTemplate(MRouteINETTestTemplate):
TOPOLOGY = {
"vnet_router": {"ifaces": ["if1", "if2", "if3", "if4"]},
"vnet_host1": {"ifaces": ["if1"]},
@@ -231,6 +228,14 @@ class Test1RCrissCrossINET(MRouteINETTestTemplate):
}
MULTICAST_ADDR = "239.0.0.1"
+
+
+class Test1RCrissCrossINET(MRouteINETCrissCrossTestTemplate):
+ """
+ Test a router connected to four hosts, with pairs of interfaces
+ in different FIBs.
+ """
+
def setup_method(self, method):
# Create VNETs and start the handlers.
super().setup_method(method)
@@ -288,6 +293,65 @@ class Test1RCrissCrossINET(MRouteINETTestTemplate):
self.waittest()
+class Test1RCrissCrossINETMissingRouter(MRouteINETCrissCrossTestTemplate):
+ """
+ Test what happens when a router is configured for some FIBs but not others.
+ """
+
+ def setup_method(self, method):
+ # Create VNETs and start the handlers.
+ super().setup_method(method)
+
+ # Only start a pimd instance in FIB 0.
+ ifaces = [self.vnet.iface_alias_map[i].name for i in ["if1", "if2"]]
+ self.pimd0 = self.run_pimd("test0", ifaces, "127.0.0.1",
+ self.MULTICAST_ADDR + "/32", fib=0)
+
+ time.sleep(3) # Give pimd a bit of time to get itself together.
+
+ def vnet_host1_handler(self, vnet):
+ self.jointest(vnet)
+
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345)
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 0!")
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Goodbye, Multicast on FIB 0!")
+ self.donetest()
+
+ def vnet_host2_handler(self, vnet):
+ self.jointest(vnet)
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345)
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Hello, Multicast on FIB 0!")
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 0!")
+ self._msgwait(self.sock, b"Goodbye, Multicast on FIB 0!")
+ self.donetest()
+
+ def vnet_host3_handler(self, vnet):
+ self.jointest(vnet)
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345)
+ timedout = False
+ try:
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 1!", timeout=5)
+ except socket.timeout:
+ timedout = True
+ assert timedout, "Received a message when we shouldn't have"
+ self.donetest()
+
+ def vnet_host4_handler(self, vnet):
+ self.jointest(vnet)
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345)
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Hello, Multicast on FIB 1!")
+ self.donetest()
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["pimd"])
+ @pytest.mark.timeout(30)
+ def test(self):
+ self.starttest(["vnet_host1", "vnet_host2", "vnet_host3", "vnet_host4"])
+ self.waittest()
+
class Test1RBasicINET6(MRouteINET6TestTemplate):
"""Basic multicast routing setup with 2 hosts connected via a router."""
@@ -343,12 +407,7 @@ class Test1RBasicINET6(MRouteINET6TestTemplate):
self.waittest()
-class Test1RCrissCrossINET6(MRouteINET6TestTemplate):
- """
- Test a router connected to four hosts, with pairs of interfaces
- in different FIBs.
- """
-
+class MRouteINET6CrissCrossTestTemplate(MRouteINET6TestTemplate):
TOPOLOGY = {
"vnet_router": {"ifaces": ["if1", "if2", "if3", "if4"]},
"vnet_host1": {"ifaces": ["if1"]},
@@ -374,6 +433,74 @@ class Test1RCrissCrossINET6(MRouteINET6TestTemplate):
}
MULTICAST_ADDR = "ff05::1"
+
+class Test1RCrissCrossINET6MissingRouter(MRouteINET6CrissCrossTestTemplate):
+ """
+ Test what happens when a router is configured for some FIBs but not others.
+ """
+
+ def setup_method(self, method):
+ # Create VNETs and start the handlers.
+ super().setup_method(method)
+
+ # Only start an ip6_mrouted instance in FIB 0.
+ ifaces = [self.vnet.iface_alias_map[i].name for i in ["if1", "if2"]]
+ self.mrouted0 = self.run_ip6_mrouted("test0", ifaces, fib=0)
+ time.sleep(1) # Give ip6_mrouted a bit of time to get itself together.
+
+ def vnet_host1_handler(self, vnet):
+ self.jointest(vnet)
+
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name)
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 0!")
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Goodbye, Multicast on FIB 0!")
+ self.donetest()
+
+ def vnet_host2_handler(self, vnet):
+ self.jointest(vnet)
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name)
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Hello, Multicast on FIB 0!")
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 0!")
+ self._msgwait(self.sock, b"Goodbye, Multicast on FIB 0!")
+ self.donetest()
+
+ def vnet_host3_handler(self, vnet):
+ self.jointest(vnet)
+
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345,
+ vnet.ifaces[0].name)
+ timedout = False
+ try:
+ self._msgwait(self.sock, b"Hello, Multicast on FIB 1!", timeout=5)
+ except socket.timeout:
+ timedout = True
+ assert timedout, "Received a message when we shouldn't have"
+ self.donetest()
+
+ def vnet_host4_handler(self, vnet):
+ self.jointest(vnet)
+
+ self.sock = self.mcast_join(self.MULTICAST_ADDR, 12345,
+ vnet.ifaces[0].name)
+ self.mcast_sendto(self.MULTICAST_ADDR, 12345, vnet.ifaces[0].name,
+ b"Hello, Multicast on FIB 1!")
+ self.donetest()
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.timeout(30)
+ def test(self):
+ self.starttest(["vnet_host1", "vnet_host2", "vnet_host3", "vnet_host4"])
+ self.waittest()
+
+
+class Test1RCrissCrossINET6(MRouteINET6CrissCrossTestTemplate):
+ """
+ Test a router connected to four hosts, with pairs of interfaces
+ in different FIBs.
+ """
+
def setup_method(self, method):
# Create VNETs and start the handlers.
super().setup_method(method)
diff --git a/tests/sys/netinet/socket_afinet.c b/tests/sys/netinet/socket_afinet.c
index e90fed961b73..cfa2c9aa1706 100644
--- a/tests/sys/netinet/socket_afinet.c
+++ b/tests/sys/netinet/socket_afinet.c
@@ -57,9 +57,6 @@ ATF_TC_BODY(socket_afinet_bind_zero, tc)
int sd, rc;
struct sockaddr_in sin;
- if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
- atf_tc_skip("doesn't work when mac_portacl(4) loaded (https://bugs.freebsd.org/238781)");
-
sd = socket(PF_INET, SOCK_DGRAM, 0);
ATF_CHECK(sd >= 0);
diff --git a/tests/sys/netpfil/pf/limiters.sh b/tests/sys/netpfil/pf/limiters.sh
index 8d9a199db787..f576955e5640 100644
--- a/tests/sys/netpfil/pf/limiters.sh
+++ b/tests/sys/netpfil/pf/limiters.sh
@@ -185,6 +185,56 @@ state_block_cleanup()
pft_cleanup
}
+atf_test_case "state_multiple" "cleanup"
+state_multiple_head()
+{
+ atf_set descr 'Create multiple state limiters'
+ atf_set require.user root
+}
+
+state_multiple_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ # Allow up to one ICMP state.
+ pft_set_rules alcatraz \
+ "set timeout icmp.error 120" \
+ "state limiter \"server\" id 1 limit 1" \
+ "state limiter \"client\" id 2 limit 1" \
+ "block in proto icmp" \
+ "pass in proto icmp state limiter \"server\" (no-match)"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 2 192.0.2.1
+
+ # This should now fail
+ atf_check -s exit:2 -o ignore \
+ ping -c 2 192.0.2.1
+
+ jexec alcatraz pfctl -sLimiterStates
+ hardlim=$(jexec alcatraz pfctl -sLimiterStates | awk 'NR>1 { print $5; }')
+ if [ $hardlim -eq 0 ]; then
+ atf_fail "Hard limit not incremented"
+ fi
+}
+
+state_multiple_cleanup()
+{
+ pft_cleanup
+}
+
atf_test_case "source_basic" "cleanup"
source_basic_head()
{
@@ -261,5 +311,6 @@ atf_init_test_cases()
atf_add_test_case "state_basic"
atf_add_test_case "state_rate"
atf_add_test_case "state_block"
+ atf_add_test_case "state_multiple"
atf_add_test_case "source_basic"
}
diff --git a/tools/build/Makefile b/tools/build/Makefile
index 604885dea4c8..f0856900b281 100644
--- a/tools/build/Makefile
+++ b/tools/build/Makefile
@@ -395,10 +395,8 @@ SYSINCS+= ${SRCTOP}/sys/sys/ctf.h
SYSINCS+= ${SRCTOP}/sys/sys/kbio.h
# for kldxref:
SYSINCS+= ${SRCTOP}/sys/sys/module.h
-.if ${.MAKE.OS} != "FreeBSD"
# for libmd:
SYSINCS+= ${SRCTOP}/sys/sys/md4.h
-.endif
# We want to run the build with only ${WORLDTMP} in $PATH to ensure we don't
# accidentally run tools that are incompatible but happen to be in $PATH.
diff --git a/tools/build/options/WITH_EXPERIMENTAL b/tools/build/options/WITH_EXPERIMENTAL
deleted file mode 100644
index a5f41addee52..000000000000
--- a/tools/build/options/WITH_EXPERIMENTAL
+++ /dev/null
@@ -1 +0,0 @@
-Include experimental features in the build.
diff --git a/tools/build/test-includes/badfiles.inc b/tools/build/test-includes/badfiles.inc
index 3c7390b22a89..a0aada4f016f 100644
--- a/tools/build/test-includes/badfiles.inc
+++ b/tools/build/test-includes/badfiles.inc
@@ -63,7 +63,6 @@ BADHDRS= \
sys/kdb.h \
sys/kernel.h \
sys/khelp.h \
- sys/kobj.h \
sys/ksem.h \
sys/ktls.h \
sys/libkern.h \
diff --git a/usr.bin/apply/apply.1 b/usr.bin/apply/apply.1
index f1c114d40814..1e0115364e81 100644
--- a/usr.bin/apply/apply.1
+++ b/usr.bin/apply/apply.1
@@ -96,7 +96,7 @@ The following environment variable affects the execution of
.Bl -tag -width SHELL
.It Ev SHELL
Pathname of shell to use.
-If this variable is not defined, the Bourne shell is used.
+If this variable is not defined, the POSIX shell is used.
.El
.Sh FILES
.Bl -tag -width /bin/sh -compact
diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1
index 0891f3f28c92..0321d748a1a4 100644
--- a/usr.bin/chpass/chpass.1
+++ b/usr.bin/chpass/chpass.1
@@ -227,7 +227,7 @@ The
field is the command interpreter the user prefers.
If the
.Ar shell
-field is empty, the Bourne shell,
+field is empty, the POSIX shell,
.Pa /bin/sh ,
is assumed.
When altering a login shell, and not the super-user, the user
diff --git a/usr.bin/du/du.1 b/usr.bin/du/du.1
index 1b6d800b0285..3b330de7bc5b 100644
--- a/usr.bin/du/du.1
+++ b/usr.bin/du/du.1
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 16, 2025
+.Dd April 15, 2026
.Dt DU 1
.Os
.Sh NAME
@@ -225,8 +225,8 @@ Also display a grand total at the end:
.Xr chflags 2 ,
.Xr fts 3 ,
.Xr libxo 3 ,
-.Xr xo_options 7 ,
.Xr symlink 7 ,
+.Xr xo_options 7 ,
.Xr quot 8
.Sh STANDARDS
The
diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c
index 16ca05fa9edc..bf138b1b5e36 100644
--- a/usr.bin/du/du.c
+++ b/usr.bin/du/du.c
@@ -35,7 +35,7 @@
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
-#include <err.h>
+
#include <errno.h>
#include <fnmatch.h>
#include <fts.h>
@@ -67,8 +67,8 @@ struct ignentry {
static bool check_threshold(FTSENT *);
static void ignoreadd(const char *);
static void ignoreclean(void);
-static int ignorep(FTSENT *);
-static int linkchk(FTSENT *);
+static bool ignorep(FTSENT *);
+static bool linkchk(FTSENT *);
static void print_file_size(FTSENT *);
static void prthumanval(const char *, int64_t);
static void record_file_size(FTSENT *);
@@ -91,6 +91,7 @@ main(int argc, char *argv[])
{
FTS *fts;
FTSENT *p;
+ int64_t num;
off_t savednumber;
int ftsoptions;
int depth;
@@ -189,11 +190,12 @@ main(int argc, char *argv[])
case 'r': /* Compatibility. */
break;
case 't':
- if (expand_number(optarg, &threshold) != 0 ||
- threshold == 0) {
+ if (expand_number(optarg, &num) != 0 || num == 0) {
xo_warnx("invalid threshold: %s", optarg);
usage();
- } else if (threshold < 0)
+ }
+ threshold = num;
+ if (threshold < 0)
threshold_sign = -1;
break;
case 'x':
@@ -239,7 +241,7 @@ main(int argc, char *argv[])
if (sflag)
depth = 0;
- if (!*argv) {
+ if (argc == 0) {
argv = save;
argv[0] = dot;
argv[1] = NULL;
@@ -262,13 +264,12 @@ main(int argc, char *argv[])
(void)signal(SIGINFO, siginfo);
if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
- err(1, "fts_open");
-
+ xo_err(1, "fts_open");
xo_set_version(DU_XO_VERSION);
xo_open_container("disk-usage-information");
xo_open_list("paths");
- while (errno = 0, (p = fts_read(fts)) != NULL) {
+ for (errno = 0; (p = fts_read(fts)) != NULL; errno = 0) {
switch (p->fts_info) {
case FTS_D: /* Ignore. */
if (ignorep(p))
@@ -313,7 +314,7 @@ main(int argc, char *argv[])
}
xo_close_list("paths");
- if (errno)
+ if (errno != 0)
xo_err(1, "fts_read");
if (cflag) {
@@ -334,7 +335,7 @@ main(int argc, char *argv[])
exit(rval);
}
-static int
+static bool
linkchk(FTSENT *p)
{
struct links_entry {
@@ -362,7 +363,7 @@ linkchk(FTSENT *p)
number_buckets = links_hash_initial_size;
buckets = malloc(number_buckets * sizeof(buckets[0]));
if (buckets == NULL)
- errx(1, "No memory for hardlink detection");
+ xo_errx(1, "No memory for hardlink detection");
for (i = 0; i < number_buckets; i++)
buckets[i] = NULL;
}
@@ -433,12 +434,12 @@ linkchk(FTSENT *p)
free_list = le;
}
}
- return (1);
+ return (true);
}
}
if (stop_allocating)
- return (0);
+ return (false);
/* Add this entry to the links cache. */
if (free_list != NULL) {
@@ -451,7 +452,7 @@ linkchk(FTSENT *p)
if (le == NULL) {
stop_allocating = 1;
xo_warnx("No more memory for tracking hard links");
- return (0);
+ return (false);
}
le->dev = st->st_dev;
le->ino = st->st_ino;
@@ -462,7 +463,7 @@ linkchk(FTSENT *p)
if (buckets[hash] != NULL)
buckets[hash]->previous = le;
buckets[hash] = le;
- return (0);
+ return (false);
}
static void
@@ -500,10 +501,10 @@ ignoreadd(const char *mask)
ign = calloc(1, sizeof(*ign));
if (ign == NULL)
- errx(1, "cannot allocate memory");
+ xo_errx(1, "cannot allocate memory");
ign->mask = strdup(mask);
if (ign->mask == NULL)
- errx(1, "cannot allocate memory");
+ xo_errx(1, "cannot allocate memory");
SLIST_INSERT_HEAD(&ignores, ign, next);
}
@@ -520,17 +521,18 @@ ignoreclean(void)
}
}
-static int
+static bool
ignorep(FTSENT *ent)
{
struct ignentry *ign;
if (nodumpflag && (ent->fts_statp->st_flags & UF_NODUMP))
- return (1);
- SLIST_FOREACH(ign, &ignores, next)
+ return (true);
+ SLIST_FOREACH(ign, &ignores, next) {
if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
- return (1);
- return (0);
+ return (true);
+ }
+ return (false);
}
static void
diff --git a/usr.bin/du/tests/du_test.sh b/usr.bin/du/tests/du_test.sh
index 27076be8dc3e..58ff1c3ecbd8 100755
--- a/usr.bin/du/tests/du_test.sh
+++ b/usr.bin/du/tests/du_test.sh
@@ -276,7 +276,7 @@ si_flag_body()
atf_check -o inline:'1.5M\tA\n1.6M\tB\n' du -A --si A B
}
-atf_add_test_case t_flag
+atf_test_case t_flag
t_flag_head()
{
atf_set "descr" "Verify -t output"
@@ -295,6 +295,23 @@ t_flag_body()
sort du.out
}
+atf_test_case stdout
+stdout_head()
+{
+ atf_set "descr" "Failure to write to stdout"
+}
+stdout_body()
+{
+ (
+ trap "" PIPE
+ sleep 1
+ du 2>stderr
+ echo $? >result
+ ) | true
+ atf_check -o inline:"1\n" cat result
+ atf_check -o match:"stdout" cat stderr
+}
+
atf_init_test_cases()
{
atf_add_test_case A_flag
@@ -314,4 +331,5 @@ atf_init_test_cases()
atf_add_test_case s_flag
atf_add_test_case si_flag
atf_add_test_case t_flag
+ atf_add_test_case stdout
}
diff --git a/usr.bin/fortune/datfiles/freebsd-tips b/usr.bin/fortune/datfiles/freebsd-tips
index 379f772fea88..47fd3c755626 100644
--- a/usr.bin/fortune/datfiles/freebsd-tips
+++ b/usr.bin/fortune/datfiles/freebsd-tips
@@ -180,7 +180,7 @@ directory sizes.
nc(1) (or netcat) is useful not only for redirecting input/output to
TCP or UDP connections, but also for proxying them with inetd(8).
%
-sh (the default Bourne shell in FreeBSD) supports command-line editing. Just
+sh (the default POSIX shell in FreeBSD) supports command-line editing. Just
``set -o emacs'' or ``set -o vi'' to enable it. Use "<TAB>" key to complete
paths.
%
@@ -415,7 +415,7 @@ if you leave the shell idle for more than 30 minutes.
%
You can use aliases to decrease the amount of typing you need to do to get
commands you commonly use. Examples of fairly popular aliases include (in
-Bourne shell style, as in /bin/sh, bash, ksh, and zsh):
+POSIX shell style, as in /bin/sh, bash, ksh, and zsh):
alias lf="ls -FA"
alias ll="ls -lA"
diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c
index c625b49dc29a..4a288d66be81 100644
--- a/usr.bin/mkimg/mkimg.c
+++ b/usr.bin/mkimg/mkimg.c
@@ -446,6 +446,8 @@ mkimg(void)
{
FILE *fp;
struct part *part;
+ struct stat sb;
+ char *p;
lba_t block, blkoffset;
uint64_t bytesize, byteoffset;
char *size, *offset;
@@ -468,12 +470,28 @@ mkimg(void)
/* Look for an offset. Set size too if we can. */
switch (part->kind) {
case PART_KIND_SIZE:
- case PART_KIND_FILE:
offset = part->contents;
size = strsep(&offset, ":");
- if (part->kind == PART_KIND_SIZE &&
- expand_number(size, &bytesize) == -1)
+ if (expand_number(size, &bytesize) == -1)
error = errno;
+ break;
+ case PART_KIND_FILE:
+ size = part->contents;
+ if (stat(part->contents, &sb) == 0) {
+ if (S_ISDIR(sb.st_mode)) {
+ errc(EX_IOERR, EISDIR, "partition %d",
+ part->index + 1);
+ }
+ offset = NULL;
+ } else {
+ p = strrchr(part->contents, ':');
+ if (p != NULL) {
+ *p = '\0';
+ offset = p + 1;
+ } else {
+ offset = NULL;
+ }
+ }
if (offset != NULL) {
if (*offset != '+')
abs_offset = true;
diff --git a/usr.bin/script/script.1 b/usr.bin/script/script.1
index 5f40e5af28ff..ef8eaa35174f 100644
--- a/usr.bin/script/script.1
+++ b/usr.bin/script/script.1
@@ -139,7 +139,7 @@ Forward terminal size changes on
The script ends when the forked shell (or command) exits (a
.Em control-D
to exit
-the Bourne shell
+the POSIX shell
.Pf ( Xr sh 1 ) ,
and
.Em exit ,
@@ -184,7 +184,7 @@ exists, the shell forked by
will be that shell.
If
.Ev SHELL
-is not set, the Bourne shell
+is not set, the POSIX shell
is assumed.
.Pq Most shells set this variable automatically .
.El
diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1
index b0fae81ee566..70513794ebd9 100644
--- a/usr.bin/sockstat/sockstat.1
+++ b/usr.bin/sockstat/sockstat.1
@@ -25,7 +25,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd October 29, 2025
+.Dd April 15, 2026
.Dt SOCKSTAT 1
.Os
.Sh NAME
@@ -261,10 +261,10 @@ $ sockstat --libxo json,pretty
.Xr netstat 1 ,
.Xr procstat 1 ,
.Xr setfib 1 ,
+.Xr libxo 3 ,
.Xr inet 4 ,
.Xr inet6 4 ,
-.Xr protocols 5
-.Xr libxo 3 ,
+.Xr protocols 5 ,
.Xr xo_options 7
.Sh HISTORY
The
diff --git a/usr.bin/unzip/Makefile b/usr.bin/unzip/Makefile
index 63f49a203685..3ca95e5fa881 100644
--- a/usr.bin/unzip/Makefile
+++ b/usr.bin/unzip/Makefile
@@ -12,7 +12,7 @@ BSDUNZIP_VERSION_STRING!= sed -n '/define.*ARCHIVE_VERSION_ONLY_STRING/{s,[^0-9.
SRCS= bsdunzip.c
.PATH: ${_LIBARCHIVEDIR}/libarchive_fe
-SRCS+= cmdline.c lafe_err.c passphrase.c
+SRCS+= cmdline.c lafe_err.c lafe_getline.c passphrase.c
CFLAGS+= -DBSDUNZIP_VERSION_STRING=\"${BSDUNZIP_VERSION_STRING}\"
CFLAGS+= -DPLATFORM_CONFIG_H=\"${_LIBARCHIVECONFDIR}/config_freebsd.h\"
diff --git a/usr.bin/yacc/config.h b/usr.bin/yacc/config.h
index c9a9fe59139b..16ee996c427c 100644
--- a/usr.bin/yacc/config.h
+++ b/usr.bin/yacc/config.h
@@ -92,7 +92,7 @@
/* #undef STDC_NORETURN */
/* Define to the system name. */
-#define SYSTEM_NAME "freebsd14.0"
+#define SYSTEM_NAME "freebsd16.0"
/* "Define to 1 if you want to use dbmalloc for testing." */
/* #undef USE_DBMALLOC */
diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8
index e21a7653f250..eacc12a8f3ce 100644
--- a/usr.sbin/adduser/adduser.8
+++ b/usr.sbin/adduser/adduser.8
@@ -459,7 +459,7 @@ command appeared in
.An -nosplit
This manual page and the original script, in Perl, was written by
.An Wolfram Schneider Aq Mt wosch@FreeBSD.org .
-The replacement script, written as a Bourne
+The replacement script, written as a POSIX
shell script with some enhancements, and the man page modification that
came with it were done by
.An Mike Makonnen Aq Mt mtm@identd.net .
diff --git a/usr.sbin/bluetooth/hccontrol/le.c b/usr.sbin/bluetooth/hccontrol/le.c
index 6d5440643b45..fbcee1451b0a 100644
--- a/usr.sbin/bluetooth/hccontrol/le.c
+++ b/usr.sbin/bluetooth/hccontrol/le.c
@@ -182,7 +182,7 @@ parse_param(int argc, char *argv[], char *buf, int *len)
uint16_t value;
optreset = 1;
optind = 0;
- while ((ch = getopt(argc, argv , "n:f:u:")) != -1) {
+ while ((ch = getopt(argc, argv , "n:f:u:b:")) != -1) {
switch(ch){
case 'n':
datalen = strlen(optarg);
@@ -218,7 +218,24 @@ parse_param(int argc, char *argv[], char *buf, int *len)
curbuf += 2;
*lenpos += 2;
}
-
+ break;
+ case 'b':
+ datalen = 1;
+ token = optarg;
+ while ((token = strchr(token, ',')) != NULL) {
+ datalen++;
+ token++;
+ }
+ if ((curbuf + datalen + 1) >= buflast)
+ goto done;
+ curbuf[0] = datalen;
+ curbuf++;
+ token = optarg;
+ while ((token = strsep(&optarg, ",")) != NULL) {
+ value = strtol(token, NULL, 16);
+ curbuf[0] = value & 0xff;
+ curbuf++;
+ }
}
}
done:
@@ -1297,7 +1314,7 @@ struct hci_command le_commands[] = {
},
{
"le_set_advertising_data",
- "le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 \n"
+ "le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 -b $byte,$byte,...,$byte\n"
"set LE device advertising packed data",
&le_set_advertising_data
},
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
index 3a5cd9d42658..f71aacd6f2e9 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c
@@ -31,6 +31,7 @@
#include <sys/endian.h>
#include <sys/stat.h>
+#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -192,3 +193,107 @@ iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix,
return (fwname);
}
+
+int
+iwmbt_parse_tlv(uint8_t *data, uint8_t datalen,
+ struct iwmbt_version_tlv *version)
+{
+ uint8_t status, type, len;
+
+ status = *data++;
+ if (status != 0)
+ return (-1);
+ datalen--;
+
+ while (datalen >= 2) {
+ type = *data++;
+ len = *data++;
+ datalen -= 2;
+
+ if (datalen < len)
+ return (-1);
+
+ switch (type) {
+ case IWMBT_TLV_CNVI_TOP:
+ assert(len == 4);
+ version->cnvi_top = le32dec(data);
+ break;
+ case IWMBT_TLV_CNVR_TOP:
+ assert(len == 4);
+ version->cnvr_top = le32dec(data);
+ break;
+ case IWMBT_TLV_CNVI_BT:
+ assert(len == 4);
+ version->cnvi_bt = le32dec(data);
+ break;
+ case IWMBT_TLV_CNVR_BT:
+ assert(len == 4);
+ version->cnvr_bt = le32dec(data);
+ break;
+ case IWMBT_TLV_DEV_REV_ID:
+ assert(len == 2);
+ version->dev_rev_id = le16dec(data);
+ break;
+ case IWMBT_TLV_IMAGE_TYPE:
+ assert(len == 1);
+ version->img_type = *data;
+ break;
+ case IWMBT_TLV_TIME_STAMP:
+ assert(len == 2);
+ version->min_fw_build_cw = data[0];
+ version->min_fw_build_yy = data[1];
+ version->timestamp = le16dec(data);
+ break;
+ case IWMBT_TLV_BUILD_TYPE:
+ assert(len == 1);
+ version->build_type = *data;
+ break;
+ case IWMBT_TLV_BUILD_NUM:
+ assert(len == 4);
+ version->min_fw_build_nn = *data;
+ version->build_num = le32dec(data);
+ break;
+ case IWMBT_TLV_SECURE_BOOT:
+ assert(len == 1);
+ version->secure_boot = *data;
+ break;
+ case IWMBT_TLV_OTP_LOCK:
+ assert(len == 1);
+ version->otp_lock = *data;
+ break;
+ case IWMBT_TLV_API_LOCK:
+ assert(len == 1);
+ version->api_lock = *data;
+ break;
+ case IWMBT_TLV_DEBUG_LOCK:
+ assert(len == 1);
+ version->debug_lock = *data;
+ break;
+ case IWMBT_TLV_MIN_FW:
+ assert(len == 3);
+ version->min_fw_build_nn = data[0];
+ version->min_fw_build_cw = data[1];
+ version->min_fw_build_yy = data[2];
+ break;
+ case IWMBT_TLV_LIMITED_CCE:
+ assert(len == 1);
+ version->limited_cce = *data;
+ break;
+ case IWMBT_TLV_SBE_TYPE:
+ assert(len == 1);
+ version->sbe_type = *data;
+ break;
+ case IWMBT_TLV_OTP_BDADDR:
+ memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
+ break;
+ default:
+ /* Ignore other types */
+ break;
+ }
+
+ datalen -= len;
+ data += len;
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
index eb6909a1f91d..1763f8688ed0 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h
@@ -152,5 +152,7 @@ extern char *iwmbt_get_fwname(struct iwmbt_version *ver,
const char *suffix);
extern char *iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver,
const char *prefix, const char *suffix);
+extern int iwmbt_parse_tlv(uint8_t *data, uint8_t datalen,
+ struct iwmbt_version_tlv *ver);
#endif
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
index 255181b8f4bc..81f1fbe7c5ce 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
@@ -30,7 +30,6 @@
#include <sys/endian.h>
#include <sys/stat.h>
-#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stddef.h>
@@ -409,6 +408,29 @@ iwmbt_load_fwfile(struct libusb_device_handle *hdl,
}
int
+iwmbt_bt_reset(struct libusb_device_handle *hdl)
+{
+ int ret, transferred;
+ static struct iwmbt_hci_cmd cmd = {
+ .opcode = htole16(0x0c03),
+ .length = 0,
+ };
+ uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE];
+
+ ret = iwmbt_hci_command(hdl,
+ &cmd,
+ buf,
+ sizeof(buf),
+ &transferred,
+ IWMBT_HCI_CMD_TIMEOUT);
+
+ if (ret < 0)
+ iwmbt_debug("HCI reset command failed: code=%d", ret);
+
+ return (ret);
+}
+
+int
iwmbt_enter_manufacturer(struct libusb_device_handle *hdl)
{
int ret, transferred;
@@ -502,8 +524,8 @@ iwmbt_get_version(struct libusb_device_handle *hdl,
}
int
-iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
- struct iwmbt_version_tlv *version)
+iwmbt_read_version_tlv(struct libusb_device_handle *hdl,
+ uint8_t *data, uint8_t *datalen)
{
int ret, transferred;
struct iwmbt_hci_event_cmd_compl *event;
@@ -512,8 +534,6 @@ iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
.length = 1,
.data = { 0xff },
};
- uint8_t status, datalen, type, len;
- uint8_t *data;
uint8_t buf[255];
memset(buf, 0, sizeof(buf));
@@ -533,106 +553,30 @@ iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
}
event = (struct iwmbt_hci_event_cmd_compl *)buf;
- memcpy(version, event->data, sizeof(struct iwmbt_version));
+ *datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
+ memcpy(data, event->data, *datalen);
- datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE;
- data = event->data;
- status = *data++;
- if (status != 0)
- return (-1);
- datalen--;
+ return (0);
+}
- while (datalen >= 2) {
- type = *data++;
- len = *data++;
- datalen -= 2;
+int
+iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
+ struct iwmbt_version_tlv *version)
+{
- if (datalen < len)
- return (-1);
+ uint8_t data[255];
+ uint8_t datalen;
+ int ret;
- switch (type) {
- case IWMBT_TLV_CNVI_TOP:
- assert(len == 4);
- version->cnvi_top = le32dec(data);
- break;
- case IWMBT_TLV_CNVR_TOP:
- assert(len == 4);
- version->cnvr_top = le32dec(data);
- break;
- case IWMBT_TLV_CNVI_BT:
- assert(len == 4);
- version->cnvi_bt = le32dec(data);
- break;
- case IWMBT_TLV_CNVR_BT:
- assert(len == 4);
- version->cnvr_bt = le32dec(data);
- break;
- case IWMBT_TLV_DEV_REV_ID:
- assert(len == 2);
- version->dev_rev_id = le16dec(data);
- break;
- case IWMBT_TLV_IMAGE_TYPE:
- assert(len == 1);
- version->img_type = *data;
- break;
- case IWMBT_TLV_TIME_STAMP:
- assert(len == 2);
- version->min_fw_build_cw = data[0];
- version->min_fw_build_yy = data[1];
- version->timestamp = le16dec(data);
- break;
- case IWMBT_TLV_BUILD_TYPE:
- assert(len == 1);
- version->build_type = *data;
- break;
- case IWMBT_TLV_BUILD_NUM:
- assert(len == 4);
- version->min_fw_build_nn = *data;
- version->build_num = le32dec(data);
- break;
- case IWMBT_TLV_SECURE_BOOT:
- assert(len == 1);
- version->secure_boot = *data;
- break;
- case IWMBT_TLV_OTP_LOCK:
- assert(len == 1);
- version->otp_lock = *data;
- break;
- case IWMBT_TLV_API_LOCK:
- assert(len == 1);
- version->api_lock = *data;
- break;
- case IWMBT_TLV_DEBUG_LOCK:
- assert(len == 1);
- version->debug_lock = *data;
- break;
- case IWMBT_TLV_MIN_FW:
- assert(len == 3);
- version->min_fw_build_nn = data[0];
- version->min_fw_build_cw = data[1];
- version->min_fw_build_yy = data[2];
- break;
- case IWMBT_TLV_LIMITED_CCE:
- assert(len == 1);
- version->limited_cce = *data;
- break;
- case IWMBT_TLV_SBE_TYPE:
- assert(len == 1);
- version->sbe_type = *data;
- break;
- case IWMBT_TLV_OTP_BDADDR:
- memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t));
- break;
- default:
- /* Ignore other types */
- break;
- }
+ memset(data, 0, sizeof(data));
- datalen -= len;
- data += len;
+ ret = iwmbt_read_version_tlv(hdl, data, &datalen);
+ if (ret < 0) {
+ iwmbt_debug("Can't get version tlv");
+ return (-1);
}
- return (0);
+ return (iwmbt_parse_tlv(data, datalen, version));
}
int
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
index aac885dfd153..f1f1f56e0ca2 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h
@@ -101,11 +101,14 @@ extern int iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw);
extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl,
const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset);
+extern int iwmbt_bt_reset(struct libusb_device_handle *hdl);
extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl);
extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl,
enum iwmbt_mm_exit mode);
extern int iwmbt_get_version(struct libusb_device_handle *hdl,
struct iwmbt_version *version);
+extern int iwmbt_read_version_tlv(struct libusb_device_handle *hdl,
+ uint8_t *data, uint8_t *datalen);
extern int iwmbt_get_version_tlv(struct libusb_device_handle *hdl,
struct iwmbt_version_tlv *version);
extern int iwmbt_get_boot_params(struct libusb_device_handle *hdl,
diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
index fd0118655a67..342656613421 100644
--- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
+++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8
@@ -26,7 +26,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 15, 2025
+.Dd April 2, 2026
.Dt IWMBTFW 8
.Os
.Sh NAME
@@ -50,7 +50,8 @@ This utility will
.Em only
work with Intel Wireless 7260/8260/9260 and newer chip based Bluetooth
USB devices, including AX and BE series wireless adapters.
-The identification is currently based on USB vendor ID/product ID pair.
+The identification is currently based on USB vendor ID/product ID pair
+and result of HCI queries.
The vendor ID should be 0x8087
.Pq Dv USB_VENDOR_INTEL2
and the product ID should be one of the supported devices.
@@ -78,6 +79,8 @@ device name.
Specify the directory containing the firmware files to search and upload.
.It Fl h
Display usage message and exit.
+.It Fl p
+Use only USB vendor ID/product ID pair for device model identification.
.El
.Sh EXIT STATUS
.Ex -std
diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c
index 1e11cc468015..560735fabd67 100644
--- a/usr.sbin/bluetooth/iwmbtfw/main.c
+++ b/usr.sbin/bluetooth/iwmbtfw/main.c
@@ -35,6 +35,7 @@
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -50,6 +51,7 @@
int iwmbt_do_debug = 0;
int iwmbt_do_info = 0;
+static bool iwmbt_do_pidvid = false;
enum iwmbt_device {
IWMBT_DEVICE_UNKNOWN,
@@ -228,6 +230,65 @@ iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver)
ver->build_num);
}
+static enum iwmbt_device
+iwmbt_identify(libusb_device_handle *hdl, enum iwmbt_device device)
+{
+ uint8_t data[255];
+ uint8_t datalen, hw_platform, hw_variant;
+ struct iwmbt_version *ver = (struct iwmbt_version *)data;
+ struct iwmbt_version_tlv ver_tlv;
+ int r;
+
+ if (device == IWMBT_DEVICE_7260) {
+ r = iwmbt_bt_reset(hdl);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_bt_reset() failed!");
+ return (IWMBT_DEVICE_UNKNOWN);
+ }
+ }
+
+ memset(data, 0, sizeof(data));
+ r = iwmbt_read_version_tlv(hdl, data, &datalen);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_read_version_tlv() failed");
+ return (IWMBT_DEVICE_UNKNOWN);
+ }
+
+ if (datalen == sizeof(*ver) && ver->hw_platform == 0x37) {
+ switch (ver->hw_variant) {
+ case 0x07:
+ case 0x08:
+ return (IWMBT_DEVICE_7260);
+ case 0x0b:
+ case 0x0c:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ return (IWMBT_DEVICE_8260);
+ default:
+ iwmbt_debug("Unsupported hw_variant (0x%2x)",
+ ver->hw_variant);
+ return (IWMBT_DEVICE_UNKNOWN);
+ }
+ }
+
+ r = iwmbt_parse_tlv(data, datalen, &ver_tlv);
+ if (r < 0) {
+ iwmbt_debug("iwmbt_parse_tlv() failed");
+ return (IWMBT_DEVICE_UNKNOWN);
+ }
+
+ hw_platform = (ver_tlv.cnvi_bt >> 8) & 0xff;
+ hw_variant = (ver_tlv.cnvi_bt >> 16) & 0x3f;
+
+ if (hw_platform != 0x37) {
+ iwmbt_debug("Unsupported hw_platform (0x%2x)", hw_platform);
+ return (IWMBT_DEVICE_UNKNOWN);
+ }
+
+ return (hw_variant < 0x17 ? IWMBT_DEVICE_8260 : IWMBT_DEVICE_9260);
+}
static int
iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path,
@@ -377,11 +438,10 @@ usage(void)
fprintf(stderr, " -f: firmware path (defaults to %s)\n",
_DEFAULT_IWMBT_FIRMWARE_PATH);
fprintf(stderr, " -I: enable informational output\n");
+ fprintf(stderr, " -p: use PID/VID for model identification\n");
exit(127);
}
-
-
/*
* Returns 0 on success.
*/
@@ -558,7 +618,6 @@ handle_9260(libusb_device_handle *hdl, char *firmware_dir)
{
int r;
uint32_t boot_param;
- struct iwmbt_version vl;
struct iwmbt_version_tlv vt;
char *firmware_path = NULL;
@@ -618,9 +677,9 @@ handle_9260(libusb_device_handle *hdl, char *firmware_dir)
/* Once device is running in operational mode we can ignore failures */
- r = iwmbt_get_version(hdl, &vl);
+ r = iwmbt_get_version_tlv(hdl, &vt);
if (r == 0)
- iwmbt_dump_version(&vl);
+ iwmbt_dump_version_tlv(&vt);
/* Apply the device configuration (DDC) parameters */
firmware_path = iwmbt_get_fwname_tlv(&vt, firmware_dir, "ddc");
@@ -673,6 +732,9 @@ main(int argc, char *argv[])
case 'I':
iwmbt_do_info = 1;
break;
+ case 'p':
+ iwmbt_do_pidvid = true;
+ break;
case 'h':
default:
usage();
@@ -730,6 +792,14 @@ main(int argc, char *argv[])
goto shutdown;
}
+ if (!iwmbt_do_pidvid) {
+ iwmbt_device = iwmbt_identify(hdl, iwmbt_device);
+ if (iwmbt_device == IWMBT_DEVICE_UNKNOWN) {
+ iwmbt_err("Failed to identify device");
+ goto shutdown;
+ }
+ }
+
switch(iwmbt_device) {
case IWMBT_DEVICE_7260:
retcode = handle_7260(hdl, firmware_dir);
diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c
index 341438730a6d..9158d6404b29 100644
--- a/usr.sbin/daemon/daemon.c
+++ b/usr.sbin/daemon/daemon.c
@@ -194,6 +194,7 @@ main(int argc, char *argv[])
/*
* Supervision mode is enabled if one of the following options are used:
+ * --output-file -o
* --child-pidfile -p
* --supervisor-pidfile -P
* --restart -r / --restart-delay -R
diff --git a/usr.sbin/diskinfo/diskinfo.8 b/usr.sbin/diskinfo/diskinfo.8
index 970bafd4f8e5..aea4c123048a 100644
--- a/usr.sbin/diskinfo/diskinfo.8
+++ b/usr.sbin/diskinfo/diskinfo.8
@@ -35,7 +35,7 @@
.Nd get information about disk device
.Sh SYNOPSIS
.Nm
-.Op Fl citSvw
+.Op Fl ciStvw
.Ar disk ...
.Nm
.Op Fl l
@@ -52,9 +52,7 @@ utility prints out information about a disk device,
and optionally runs a naive performance test on the device.
.Pp
The following options are available:
-.Bl -tag -width ".Fl v"
-.It Fl v
-Print fields one per line with a descriptive comment.
+.Bl -tag -width "-c"
.It Fl c
Perform a simple measurement of the I/O read command overhead.
.It Fl i
@@ -70,16 +68,18 @@ character as a separator.
Return the physical path of the disk.
This is a string that identifies the physical path to the disk in the
storage enclosure.
-.It Fl s
-Return the disk ident, usually the serial number.
.It Fl S
Perform synchronous random write test (ZFS SLOG test),
measuring time required to write data blocks of different size and
flush disk cache.
Blocks of more then 128KB are written with multiple parallel operations.
+.It Fl s
+Return the disk ident, usually the serial number.
.It Fl t
Perform a simple and rather naive benchmark of the disks seek
and transfer performance.
+.It Fl v
+Print fields one per line with a descriptive comment.
.It Fl w
Allow disruptive write tests.
.El
diff --git a/usr.sbin/diskinfo/diskinfo.c b/usr.sbin/diskinfo/diskinfo.c
index f091d0ccfbea..d8a79d430edb 100644
--- a/usr.sbin/diskinfo/diskinfo.c
+++ b/usr.sbin/diskinfo/diskinfo.c
@@ -58,7 +58,7 @@
static void
usage(void)
{
- fprintf(stderr, "usage: diskinfo [-ciStvw] disk ...\n"
+ fprintf(stderr, "usage: diskinfo [-citSvw] disk ...\n"
" diskinfo [-l] -p disk ...\n"
" diskinfo [-l] -s disk ...\n"
);
@@ -91,7 +91,7 @@ main(int argc, char **argv)
u_int sectorsize, fwsectors, fwheads, zoned = 0, isreg;
uint32_t zone_mode;
- while ((ch = getopt(argc, argv, "cilpsStvw")) != -1) {
+ while ((ch = getopt(argc, argv, "cilpSstvw")) != -1) {
switch (ch) {
case 'c':
opt_c = 1;
@@ -107,13 +107,13 @@ main(int argc, char **argv)
case 'p':
opt_p = 1;
break;
- case 's':
- opt_s = 1;
- break;
case 'S':
opt_S = 1;
opt_v = 1;
break;
+ case 's':
+ opt_s = 1;
+ break;
case 't':
opt_t = 1;
opt_v = 1;
diff --git a/usr.sbin/efibootmgr/efibootmgr.8 b/usr.sbin/efibootmgr/efibootmgr.8
index e5054023e40c..62ca0d6da3b2 100644
--- a/usr.sbin/efibootmgr/efibootmgr.8
+++ b/usr.sbin/efibootmgr/efibootmgr.8
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 22, 2024
+.Dd April 11, 2026
.Dt EFIBOOTMGR 8
.Os
.Sh NAME
@@ -84,7 +84,7 @@ boot method to be tried once upon the next boot.
The UEFI standard defines how hosts may control what is used to
bootstrap the system.
Each method is encapsulated within a persistent UEFI variable,
-stored by the UEFI BIOS of the form
+stored by the UEFI firmware of the form
.Cm Boot Ns Em XXXX
(where XXXX are uppercase hexadecimal digits).
These variables are numbered, each describing where to load the bootstrap
diff --git a/usr.sbin/mfiutil/Makefile b/usr.sbin/mfiutil/Makefile
index 49c0e688e8e2..d7ccb1f2d9c1 100644
--- a/usr.sbin/mfiutil/Makefile
+++ b/usr.sbin/mfiutil/Makefile
@@ -9,7 +9,7 @@ MLINKS= mfiutil.8 mrsasutil.8
CFLAGS.gcc+= -fno-builtin-strftime
-LIBADD= sbuf util
+LIBADD= cam sbuf util
# Here be dragons
.ifdef DEBUG
diff --git a/usr.sbin/mfiutil/mfi_drive.c b/usr.sbin/mfiutil/mfi_drive.c
index c7c5aeb02f14..88d24623ae27 100644
--- a/usr.sbin/mfiutil/mfi_drive.c
+++ b/usr.sbin/mfiutil/mfi_drive.c
@@ -42,6 +42,7 @@
#include <string.h>
#include <strings.h>
#include <unistd.h>
+#include <cam/cam.h>
#include <cam/scsi/scsi_all.h>
#include "mfiutil.h"
@@ -279,50 +280,14 @@ mfi_pd_get_info(int fd, uint16_t device_id, struct mfi_pd_info *info,
sizeof(struct mfi_pd_info), mbox, 2, statusp));
}
-static void
-cam_strvis(char *dst, const char *src, int srclen, int dstlen)
-{
-
- /* Trim leading/trailing spaces, nulls. */
- while (srclen > 0 && src[0] == ' ')
- src++, srclen--;
- while (srclen > 0
- && (src[srclen-1] == ' ' || src[srclen-1] == '\0'))
- srclen--;
-
- while (srclen > 0 && dstlen > 1) {
- char *cur_pos = dst;
-
- if (*src < 0x20) {
- /* SCSI-II Specifies that these should never occur. */
- /* non-printable character */
- if (dstlen > 4) {
- *cur_pos++ = '\\';
- *cur_pos++ = ((*src & 0300) >> 6) + '0';
- *cur_pos++ = ((*src & 0070) >> 3) + '0';
- *cur_pos++ = ((*src & 0007) >> 0) + '0';
- } else {
- *cur_pos++ = '?';
- }
- } else {
- /* normal character */
- *cur_pos++ = *src;
- }
- src++;
- srclen--;
- dstlen -= cur_pos - dst;
- dst = cur_pos;
- }
- *dst = '\0';
-}
-
/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
const char *
mfi_pd_inq_string(struct mfi_pd_info *info)
{
struct scsi_inquiry_data iqd, *inq_data = &iqd;
- char vendor[16], product[48], revision[16], rstr[12], serial[SID_VENDOR_SPECIFIC_0_SIZE];
- static char inq_string[64];
+ char vendor[SID_VENDOR_SIZE+1], product[SID_PRODUCT_SIZE+1],
+ revision[SID_REVISION_SIZE+1], rstr[9], serial[SID_VENDOR_SPECIFIC_0_SIZE+1];
+ static char inq_string[80];
memcpy(inq_data, info->inquiry_data,
(sizeof (iqd) < sizeof (info->inquiry_data))?
@@ -334,14 +299,14 @@ mfi_pd_inq_string(struct mfi_pd_info *info)
if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED)
return (NULL);
- cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor),
- sizeof(vendor));
- cam_strvis(product, inq_data->product, sizeof(inq_data->product),
- sizeof(product));
- cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision),
- sizeof(revision));
- cam_strvis(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
- sizeof(serial));
+ cam_strvis_flag(vendor, inq_data->vendor, sizeof(inq_data->vendor),
+ sizeof(vendor), CAM_STRVIS_FLAG_NONASCII_TRIM);
+ cam_strvis_flag(product, inq_data->product, sizeof(inq_data->product),
+ sizeof(product), CAM_STRVIS_FLAG_NONASCII_TRIM);
+ cam_strvis_flag(revision, inq_data->revision, sizeof(inq_data->revision),
+ sizeof(revision), CAM_STRVIS_FLAG_NONASCII_TRIM);
+ cam_strvis_flag(serial, (char *)inq_data->vendor_specific0, sizeof(inq_data->vendor_specific0),
+ sizeof(serial), CAM_STRVIS_FLAG_NONASCII_SPC);
/* Hack for SATA disks, no idea how to tell speed. */
if (strcmp(vendor, "ATA") == 0) {
diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile
index 998860d00a69..aad2380bc879 100644
--- a/usr.sbin/ndp/Makefile
+++ b/usr.sbin/ndp/Makefile
@@ -23,11 +23,6 @@ LIBADD= xo
CFLAGS+= -I. -I${.CURDIR}
CFLAGS+= -D_U_=""
-.if ${MK_EXPERIMENTAL} != "no"
-CFLAGS+= -DEXPERIMENTAL
-CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
-.endif
-
.if ${MK_NETLINK_SUPPORT} != "no"
SRCS+= ndp_netlink.c
.else
diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c
index cbca8ec20941..7521be752c80 100644
--- a/usr.sbin/ndp/ndp.c
+++ b/usr.sbin/ndp/ndp.c
@@ -1225,12 +1225,7 @@ rtrlist(void)
*pflags++ = 'O';
xo_emit("{el:%s}", "other");
}
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- if (p->flags & ND_RA_FLAG_IPV6_ONLY) {
- *pflags++ = 'S';
- xo_emit("{el:%s}", "ipv6only");
- }
-#endif
+
xo_close_list("flags_pretty");
xo_emit(", flags={:flags/%s}", rflags);
diff --git a/usr.sbin/newsyslog/newsyslog.conf.5 b/usr.sbin/newsyslog/newsyslog.conf.5
index 1683a1018f9e..b26df4bb1a61 100644
--- a/usr.sbin/newsyslog/newsyslog.conf.5
+++ b/usr.sbin/newsyslog/newsyslog.conf.5
@@ -18,7 +18,7 @@
.\" the suitability of this software for any purpose. It is
.\" provided "as is" without express or implied warranty.
.\"
-.Dd March 8, 2026
+.Dd April 15, 2026
.Dt NEWSYSLOG.CONF 5
.Os
.Sh NAME
@@ -155,10 +155,10 @@ When the size of the log file reaches
.Ar size ,
in kilobytes by default, or with suffixes like k, M, G, ... as supported by
.Xr expand_number 3 ,
-the log file will be trimmed as described above.
+the log file will be rotated.
If this field contains an asterisk
.Pq Ql * ,
-the log file will not be trimmed based on size.
+the log file will not be rotated based on size.
.It Ar when
The
.Ar when
@@ -183,10 +183,10 @@ Additionally, the format may also be constructed with a
sign along with a rotation time specification of once
a day, once a week, or once a month.
.Pp
-Time based trimming happens only if
+Time based rotation happens only if
.Xr newsyslog 8
is run within one hour of the specified time.
-If an interval is specified, the log file will be trimmed if that many
+If an interval is specified, the log file will be rotated if that many
hours have passed since the last rotation.
When both a time and an interval are
specified then both conditions must be satisfied for the rotation to
diff --git a/usr.sbin/nfsd/nfsv4.4 b/usr.sbin/nfsd/nfsv4.4
index e96e507e23ad..9ec775613b47 100644
--- a/usr.sbin/nfsd/nfsv4.4
+++ b/usr.sbin/nfsd/nfsv4.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 8, 2024
+.Dd April, 8, 2026
.Dt NFSV4 4
.Os
.Sh NAME
@@ -106,16 +106,23 @@ The
allows a limited subset of operations to be performed on non-exported subtrees
of the local file system, so that traversal of the tree to the exported
subtrees is possible.
-As such, the ``<rootdir>'' can be in a non-exported file system.
+As such, the
+.Ql <rootdir>
+can be in a non-exported file system.
The exception is ZFS, which checks exports and, as such, all ZFS file systems
-below the ``<rootdir>'' must be exported.
+below the
+.Ql <rootdir>
+must be exported.
However,
the entire tree that is rooted at that point must be in local file systems
that are of types that can be NFS exported.
Since the
.Nm
-file system is rooted at ``<rootdir>'', setting this to anything other
-than ``/'' will result in clients being required to use different mount
+file system is rooted at
+.Ql <rootdir> ,
+setting this to anything other than
+.Ql /
+will result in clients being required to use different mount
paths for
.Nm
than for NFS Version 2 or 3.
@@ -132,9 +139,12 @@ take the form:
<user>@<dns.domain>
.Ed
.sp
-where ``<dns.domain>'' is not the same as the DNS domain used
+where
+.Ql <dns.domain>
+is not the same as the DNS domain used
for host name lookups, but is usually set to the same string.
-Most systems set this ``<dns.domain>''
+Most systems set this
+.Ql <dns.domain>
to the domain name part of the machine's
.Xr hostname 1
by default.
@@ -154,26 +164,32 @@ either client or server, this daemon must be running.
The form where the numbers are in the strings can only be used for AUTH_SYS.
To configure your systems this way, the
.Xr nfsuserd 8
-daemon does not need to be running on the server, but the following sysctls
-need to be set to 1 on the server.
+daemon should not be running on the NFS systems,
+but the following sysctls
+need to be set to 1 on the NFS server.
.sp
.Bd -literal -offset indent -compact
vfs.nfs.enable_uidtostring
vfs.nfsd.enable_stringtouid
.Ed
.sp
-On the client, the sysctl
+However, on the NFS client
+only the sysctl
.sp
.Bd -literal -offset indent -compact
vfs.nfs.enable_uidtostring
.Ed
.sp
-must be set to 1 and the
-.Xr nfsuserd 8
-daemon does not need to be running.
+needs to be set to 1.
.Pp
-If these strings are not configured correctly, ``ls -l'' will typically
-report a lot of ``nobody'' and ``nogroup'' ownerships.
+If these strings are not configured correctly,
+.Ql ls -l
+will typically
+report a lot of
+.Ql nobody
+and
+.Ql nogroup
+ownerships.
.Pp
Although uid/gid numbers are no longer used in the
.Nm
@@ -204,8 +220,12 @@ plus
nfsuserd_enable="YES"
.Ed
.sp
-if the server is using the ``<user>@<domain>'' form of user/group strings or
-is using the ``-manage-gids'' option for
+if the server is using the
+.Ql <user>@<domain>
+form of user/group strings or
+is using the
+.Ql -manage-gids
+option for
.Xr nfsuserd 8 .
.Pp
In addition, you can set:
@@ -216,7 +236,9 @@ nfsv4_server_only="YES"
.sp
to disable support for NFSv2 and NFSv3.
.Pp
-You will also need to add at least one ``V4:'' line to the
+You will also need to add at least one
+.Ql V4:
+line to the
.Xr exports 5
file for
.Nm
@@ -246,7 +268,9 @@ Disabling local locking can only be done if neither local accesses
to the exported file systems nor the NLM is operating on them.
.El
.sp
-Note that Samba server access would be considered ``local access'' for the above
+Note that Samba server access would be considered
+.Ql local access
+for the above
discussion.
.Pp
To build a kernel with the NFS server that supports
@@ -263,12 +287,16 @@ file.
.Sh CLIENT MOUNTS
To do an
.Nm
-mount, specify the ``nfsv4'' option on the
+mount, specify the
+.Ql nfsv4
+option on the
.Xr mount_nfs 8
command line.
This will force use of the client that supports
.Nm
-plus set ``tcp'' and
+plus set
+.Ql tcp
+and
.Nm .
.Pp
The
@@ -326,8 +354,8 @@ where the first 4 Ns are the host IP address and the last two are the
port# in network byte order (all decimal #s in the range 0-255).
.Pp
For NFSv4.1 and NFSv4.2, the callback path (called a backchannel) uses the
-same TCP connection as the mount, so none of the above applies and should
-work through gateways without any issues.
+same TCP connection as the mount and, as such, no additional
+configuration is needed for the callback path.
.Pp
To build a kernel with the client that supports
.Nm
@@ -345,7 +373,10 @@ Options can be specified for the
.Xr nfsuserd 8
and
.Xr nfscbd 8
-daemons at boot time via the ``nfsuserd_flags'' and ``nfscbd_flags''
+daemons at boot time via the
+.Ql nfsuserd_flags
+and
+.Ql nfscbd_flags
.Xr rc.conf 5
variables.
.Pp
@@ -356,6 +387,105 @@ It occurs when an nfsd thread tries to do an NFSv4
/ Close RPC as part of acquiring a new vnode.
If all other nfsd threads are blocked waiting for lock(s) held by this nfsd
thread, then there is no nfsd thread to service the Close RPC.
+.Sh NFSv4 ROOT FILE SYSTEM
+.Pp
+For an
+.Nm
+root file system, there are a few things that need
+to be done beyond what is needed for an NFSv3 root file system.
+The NFS server must be configured with the
+.Ql <rootdir>
+as
+.Pa /
+so that
+the mount path is the same for
+.Nm
+as NFSv3.
+This is required because the bootstrap code still uses NFSv3.
+.Pp
+The following additional changes are required in the exported
+root file system.
+.sp
+In
+.Xr fstab 5 ,
+the
+.Ql nfsv4
+mount option must be specified in
+the line for the root mount.
+.sp
+In
+.Pa /boot/loader.conf
+the line
+.sp
+.Bd -literal -offset indent -compact
+boot.nfsroot.options="nfsv4"
+.Ed
+.sp
+is needed to tell the kernel to use
+.Nm
+for the root mount.
+Additional mount options may be specified.
+.sp
+If your
+.Nm
+setup is using the form where the uid/gid numbers are in strings,
+the sysctl
+.Ql vfs.nfs.enable_uidtostring
+must be set to one before any NFSv4 read/write mounts are done.
+The recommended way to do this is to put
+.sp
+.Bd -literal -offset indent -compact
+vfs.nfs.enable_uidtostring=1
+.Ed
+.sp
+in
+.Pa /etc/sysctl.conf
+in the FS exported root file system.
+Alternately, if your
+.Nm
+setup is using
+.Ql <user>@<dns.domain>
+via
+.Xr nfsuserd 8
+for uids/gids, then you need the following additional line in
+.Pa /boot/loader.conf
+.sp
+.Bd -literal -offset indent -compact
+boot.nfsroot.user_domain="<dns.domain>"
+.Ed
+.sp
+which tells the booting kernel that you are going to be
+doing mapping via
+.Xr nfsuserd 8
+and what your
+.Ql <dns.domain>
+is.
+The booting kernel will set a few critical mappings to allow
+the boot to proceed until the
+.Xr nfsuserd 8
+daemon is started.
+Then in
+.Pa /etc/rc.conf
+you need the lines
+.sp
+.Bd -literal -offset indent -compact
+nfsuserd_enable="YES"
+nfsuserd_flags="-domain <dns.domain>"
+.Ed
+.sp
+to start the daemon.
+.Pp
+Beyond this, the setup should be the same as would be used
+for an NFSv3 root file system.
+See section
+.Ql Diskless Operation with PXE
+in the
+.Ql Advanced Networking
+chapter of the
+.Ql FreeBSD Handbook ,
+although configuring hosts as fixed addresses in your
+.Ql dhcpd.conf
+might be preferable.
.Sh FILES
.Bl -tag -width /var/db/nfs-stablerestart.bak -compact
.It Pa /var/db/nfs-stablerestart
diff --git a/usr.sbin/periodic/etc/daily/404.status-zfs b/usr.sbin/periodic/etc/daily/404.status-zfs
index 052f794c0bbc..64b67a2dc008 100755
--- a/usr.sbin/periodic/etc/daily/404.status-zfs
+++ b/usr.sbin/periodic/etc/daily/404.status-zfs
@@ -25,13 +25,15 @@ case "$daily_status_zfs_enable" in
;;
esac
sout=`zpool status -x`
- echo "$sout"
# zpool status -x always exits with 0, so we have to interpret its
# output to see what's going on.
if [ "$sout" = "all pools are healthy" \
-o "$sout" = "no pools available" ]; then
+ echo "$sout"
rc=0
else
+ # Show verbose status so we can see which files are affected
+ zpool status -v
rc=1
fi
;;
diff --git a/usr.sbin/pmcstat/pmcstat.8 b/usr.sbin/pmcstat/pmcstat.8
index edb9ff106092..c78a13f910f9 100644
--- a/usr.sbin/pmcstat/pmcstat.8
+++ b/usr.sbin/pmcstat/pmcstat.8
@@ -23,7 +23,7 @@
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
-.Dd April 19, 2025
+.Dd April 15, 2026
.Dt PMCSTAT 8
.Os
.Sh NAME
@@ -220,6 +220,30 @@ specified in
.It Fl R Ar logfilename
Perform offline analysis using sampling data in file
.Ar logfilename .
+Each decoded record is printed as a single line with the following fields:
+a record type (e.g.,
+.Dq callchain ,
+.Dq initlog ) ,
+type-specific data, and a trailing 20-digit raw TSC value recording the
+CPU cycle at which the event occurred.
+The
+.Dq initlog
+record additionally prints
+.Dq Li tsc_freq=<hz> ,
+the TSC tick rate in Hz measured by the kernel at boot.
+To convert a TSC delta to nanoseconds:
+.Pp
+.Dl elapsed_ns = (tsc_end - tsc_start) * 1e9 / tsc_freq
+.Pp
+TSC-based timestamps and
+.Dq Li tsc_freq
+are only meaningful on x86 architectures
+.Pq amd64 and i386 .
+On all other architectures
+.Pq including arm64 and powerpc
+the
+.Dq Li tsc_freq
+field will be zero.
.It Fl S Ar event-spec
Allocate a system mode sampling PMC measuring hardware events
specified in
diff --git a/usr.sbin/pmcstat/pmcstat.h b/usr.sbin/pmcstat/pmcstat.h
index 67571ce4b2ee..6131d7a50404 100644
--- a/usr.sbin/pmcstat/pmcstat.h
+++ b/usr.sbin/pmcstat/pmcstat.h
@@ -50,8 +50,9 @@
#define PMCSTAT_PRINT_ENTRY(T,...) do { \
(void) fprintf(args.pa_printfile, "%-9s", T); \
- (void) fprintf(args.pa_printfile, " " __VA_ARGS__); \
- (void) fprintf(args.pa_printfile, "\n"); \
+ (void) fprintf(args.pa_printfile, " " __VA_ARGS__); \
+ (void) fprintf(args.pa_printfile, " %20ju\n", \
+ (uintmax_t)_pmcstat_current_tsc); \
} while (0)
#define PMCSTAT_PL_NONE 0
diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c
index db9cbfabd9da..9af2ccc4e365 100644
--- a/usr.sbin/pmcstat/pmcstat_log.c
+++ b/usr.sbin/pmcstat/pmcstat_log.c
@@ -194,6 +194,7 @@ static struct pmc_plugins plugins[] = {
};
static int pmcstat_mergepmc;
+static uint64_t _pmcstat_current_tsc; /* TSC for PMCSTAT_PRINT_ENTRY */
int pmcstat_pmcinfilter = 0; /* PMC filter for top mode. */
float pmcstat_threshold = 0.5; /* Cost filter for top mode. */
@@ -471,6 +472,7 @@ pmcstat_print_log(void)
while (pmclog_read(args.pa_logparser, &ev) == 0) {
assert(ev.pl_state == PMCLOG_OK);
+ _pmcstat_current_tsc = ev.pl_tsc;
switch (ev.pl_type) {
case PMCLOG_TYPE_CALLCHAIN:
PMCSTAT_PRINT_ENTRY("callchain",
@@ -496,9 +498,11 @@ pmcstat_print_log(void)
PMCSTAT_PRINT_ENTRY("drop",);
break;
case PMCLOG_TYPE_INITIALIZE:
- PMCSTAT_PRINT_ENTRY("initlog","0x%x \"%s\"",
+ PMCSTAT_PRINT_ENTRY("initlog",
+ "0x%x \"%s\" tsc_freq=%" PRIu64,
ev.pl_u.pl_i.pl_version,
- pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch));
+ pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch),
+ ev.pl_u.pl_i.pl_tsc_freq);
if ((ev.pl_u.pl_i.pl_version & 0xFF000000) !=
PMC_VERSION_MAJOR << 24)
warnx(
diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile
index 583f9db88ceb..4ed2d7182a6b 100644
--- a/usr.sbin/rtadvd/Makefile
+++ b/usr.sbin/rtadvd/Makefile
@@ -20,11 +20,6 @@ MAN= rtadvd.conf.5 rtadvd.8
SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c timer_subr.c \
control.c control_server.c
-.if ${MK_EXPERIMENTAL} != "no"
-CFLAGS+= -DEXPERIMENTAL
-CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
-.endif
-
LIBADD= util
WARNS?= 1
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
index c0af8f76ca49..8b7079c17822 100644
--- a/usr.sbin/rtadvd/config.c
+++ b/usr.sbin/rtadvd/config.c
@@ -442,10 +442,6 @@ getconfig(struct ifinfo *ifi)
}
val |= ND_RA_FLAG_RTPREF_LOW;
}
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- if (strchr(flagstr, 'S'))
- val |= ND_RA_FLAG_IPV6_ONLY;
-#endif
} else
MAYHAVE(val, "raflags", 0);
@@ -461,9 +457,6 @@ getconfig(struct ifinfo *ifi)
__func__, rai->rai_rtpref, ifi->ifi_ifname);
goto getconfig_free_rai;
}
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- rai->rai_ipv6onlyflg = val & ND_RA_FLAG_IPV6_ONLY;
-#endif
MAYHAVE(val, "rltime", rai->rai_maxinterval * 3);
if ((uint16_t)val && ((uint16_t)val < rai->rai_maxinterval ||
@@ -1028,12 +1021,15 @@ getconfig_free_prf64:
} else {
struct rdnss *rdn;
struct dnssl *dns;
+ struct rtinfo *rti;
rai_old->rai_lifetime = 0;
TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next)
rdn->rd_ltime = 0;
TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next)
dns->dn_ltime = 0;
+ TAILQ_FOREACH(rti, &rai_old->rai_route, rti_next)
+ rti->rti_ltime = 0;
ifi->ifi_rainfo_trans = rai_old;
ifi->ifi_state = IFI_STATE_TRANSITIVE;
@@ -1485,10 +1481,6 @@ make_packet(struct rainfo *rai)
rai->rai_managedflg ? ND_RA_FLAG_MANAGED : 0;
ra->nd_ra_flags_reserved |=
rai->rai_otherflg ? ND_RA_FLAG_OTHER : 0;
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- ra->nd_ra_flags_reserved |=
- rai->rai_ipv6onlyflg ? ND_RA_FLAG_IPV6_ONLY : 0;
-#endif
ra->nd_ra_router_lifetime = htons(rai->rai_lifetime);
ra->nd_ra_reachable = htonl(rai->rai_reachabletime);
ra->nd_ra_retransmit = htonl(rai->rai_retranstimer);
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
index 1eb8f12a7338..b772f78845d8 100644
--- a/usr.sbin/rtadvd/rtadvd.c
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -138,6 +138,7 @@ union nd_opt {
#define NDOPT_FLAG_RDNSS (1 << 5)
#define NDOPT_FLAG_DNSSL (1 << 6)
#define NDOPT_FLAG_PREF64 (1 << 7)
+#define NDOPT_FLAG_ROUTEINFO (1 << 8)
static uint32_t ndopt_flags[] = {
[ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR,
@@ -148,6 +149,7 @@ static uint32_t ndopt_flags[] = {
[ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS,
[ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL,
[ND_OPT_PREF64] = NDOPT_FLAG_PREF64,
+ [ND_OPT_ROUTE_INFO] = NDOPT_FLAG_ROUTEINFO,
};
static void rtadvd_shutdown(void);
@@ -372,6 +374,7 @@ rtadvd_shutdown(void)
struct rainfo *rai;
struct rdnss *rdn;
struct dnssl *dns;
+ struct rtinfo *rti;
if (wait_shutdown) {
syslog(LOG_INFO,
@@ -416,6 +419,8 @@ rtadvd_shutdown(void)
rdn->rd_ltime = 0;
TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next)
dns->dn_ltime = 0;
+ TAILQ_FOREACH(rti, &rai->rai_route, rti_next)
+ rti->rti_ltime = 0;
}
TAILQ_FOREACH(ifi, &ifilist, ifi_next) {
if (!ifi->ifi_persist)
@@ -1085,7 +1090,8 @@ ra_input(int len, struct nd_router_advert *nra,
error = nd6_options((struct nd_opt_hdr *)(nra + 1),
len - sizeof(struct nd_router_advert), &ndopts,
NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
- NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL | NDOPT_FLAG_PREF64);
+ NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL | NDOPT_FLAG_PREF64 |
+ NDOPT_FLAG_ROUTEINFO);
if (error) {
syslog(LOG_INFO,
"<%s> ND option check failed for an RA from %s on %s",
@@ -1148,19 +1154,6 @@ ra_input(int len, struct nd_router_advert *nra,
sizeof(ntopbuf)), on_off[rai->rai_otherflg]);
inconsistent++;
}
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- /* S "IPv6-Only" (Six, Silence-IPv4) flag */
- if ((nra->nd_ra_flags_reserved & ND_RA_FLAG_IPV6_ONLY) !=
- rai->rai_ipv6onlyflg) {
- syslog(LOG_NOTICE,
- "S flag inconsistent on %s:"
- " %s from %s, %s from us",
- ifi->ifi_ifname, on_off[!rai->rai_ipv6onlyflg],
- inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf,
- sizeof(ntopbuf)), on_off[rai->rai_ipv6onlyflg]);
- inconsistent++;
- }
-#endif
/* Reachable Time */
reachabletime = ntohl(nra->nd_ra_reachable);
if (reachabletime && rai->rai_reachabletime &&
@@ -1431,7 +1424,8 @@ nd6_options(struct nd_opt_hdr *hdr, int limit,
if (hdr->nd_opt_type > ND_OPT_MTU &&
hdr->nd_opt_type != ND_OPT_RDNSS &&
hdr->nd_opt_type != ND_OPT_DNSSL &&
- hdr->nd_opt_type != ND_OPT_PREF64) {
+ hdr->nd_opt_type != ND_OPT_PREF64 &&
+ hdr->nd_opt_type != ND_OPT_ROUTE_INFO) {
syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
__func__, hdr->nd_opt_type);
continue;
@@ -1465,6 +1459,11 @@ nd6_options(struct nd_opt_hdr *hdr, int limit,
case ND_OPT_PREFIX_INFORMATION:
if (optlen == sizeof(struct nd_opt_prefix_info))
break;
+ goto skip;
+ case ND_OPT_ROUTE_INFO:
+ if (optlen >= 8 && optlen <= 24 &&
+ (optlen - sizeof(struct nd_opt_route_info)) % 8 == 0)
+ break;
skip:
syslog(LOG_INFO, "<%s> invalid option length",
__func__);
@@ -1477,6 +1476,7 @@ skip:
case ND_OPT_RDNSS:
case ND_OPT_DNSSL:
case ND_OPT_PREF64:
+ case ND_OPT_ROUTE_INFO:
break; /* we don't care about these options */
case ND_OPT_SOURCE_LINKADDR:
case ND_OPT_MTU:
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
index 5ecfd1b56423..5b9037b2dcf3 100644
--- a/usr.sbin/rtadvd/rtadvd.h
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -204,9 +204,6 @@ struct rainfo {
uint16_t rai_mininterval; /* MinRtrAdvInterval */
int rai_managedflg; /* AdvManagedFlag */
int rai_otherflg; /* AdvOtherConfigFlag */
-#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- int rai_ipv6onlyflg; /* AdvIPv6OnlyFlag */
-#endif
int rai_rtpref; /* router preference */
uint32_t rai_linkmtu; /* AdvLinkMTU */
diff --git a/usr.sbin/virtual_oss/virtual_oss/virtual_oss.8 b/usr.sbin/virtual_oss/virtual_oss/virtual_oss.8
index 6aa9f1289b35..b607f45cd369 100644
--- a/usr.sbin/virtual_oss/virtual_oss/virtual_oss.8
+++ b/usr.sbin/virtual_oss/virtual_oss/virtual_oss.8
@@ -23,7 +23,7 @@
.\" SUCH DAMAGE.
.\"
.\"
-.Dd September 3, 2024
+.Dd April 17, 2026
.Dt VIRTUAL_OSS 8
.Os
.Sh NAME
@@ -325,17 +325,6 @@ server:
virtual_oss -S -b 16 -C 2 -c 2 -r 48000 -s 4ms \\
-f /dev/sndio/default -d dsp
.Ed
-.Pp
-How to set intel based CPUs in performance mode:
-.Bd -literal -offset indent
-sysctl -aN | fgrep dev.hwpstate | fgrep epp | \
-while read OID
-do
-sysctl ${OID}=0
-done
-
-sysctl kern.sched.preempt_thresh=224
-.Ed
.Sh NOTES
All character devices are created using the 0666 mode which gives
everyone in the system access.