Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller: "Another merge window, another pull full of stuff: 1) Support alternative names for network devices, from Jiri Pirko. 2) Introduce per-netns netdev notifiers, also from Jiri Pirko. 3) Support MSG_PEEK in vsock/virtio, from Matias Ezequiel Vara Larsen. 4) Allow compiling out the TLS TOE code, from Jakub Kicinski. 5) Add several new tracepoints to the kTLS code, also from Jakub. 6) Support set channels ethtool callback in ena driver, from Sameeh Jubran. 7) New SCTP events SCTP_ADDR_ADDED, SCTP_ADDR_REMOVED, SCTP_ADDR_MADE_PRIM, and SCTP_SEND_FAILED_EVENT. From Xin Long. 8) Add XDP support to mvneta driver, from Lorenzo Bianconi. 9) Lots of netfilter hw offload fixes, cleanups and enhancements, from Pablo Neira Ayuso. 10) PTP support for aquantia chips, from Egor Pomozov. 11) Add UDP segmentation offload support to igb, ixgbe, and i40e. From Josh Hunt. 12) Add smart nagle to tipc, from Jon Maloy. 13) Support L2 field rewrite by TC offloads in bnxt_en, from Venkat Duvvuru. 14) Add a flow mask cache to OVS, from Tonghao Zhang. 15) Add XDP support to ice driver, from Maciej Fijalkowski. 16) Add AF_XDP support to ice driver, from Krzysztof Kazimierczak. 17) Support UDP GSO offload in atlantic driver, from Igor Russkikh. 18) Support it in stmmac driver too, from Jose Abreu. 19) Support TIPC encryption and auth, from Tuong Lien. 20) Introduce BPF trampolines, from Alexei Starovoitov. 21) Make page_pool API more numa friendly, from Saeed Mahameed. 22) Introduce route hints to ipv4 and ipv6, from Paolo Abeni. 23) Add UDP segmentation offload to cxgb4, Rahul Lakkireddy" * git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1857 commits) libbpf: Fix usage of u32 in userspace code mm: Implement no-MMU variant of vmalloc_user_node_flags slip: Fix use-after-free Read in slip_open net: dsa: sja1105: fix sja1105_parse_rgmii_delays() macvlan: schedule bc_work even if error enetc: add support Credit Based Shaper(CBS) for hardware offload net: phy: add helpers phy_(un)lock_mdio_bus mdio_bus: don't use managed reset-controller ax88179_178a: add ethtool_op_get_ts_info() mlxsw: spectrum_router: Fix use of uninitialized adjacency index mlxsw: spectrum_router: After underlay moves, demote conflicting tunnels bpf: Simplify __bpf_arch_text_poke poke type handling bpf: Introduce BPF_TRACE_x helper for the tracing tests bpf: Add bpf_jit_blinding_enabled for !CONFIG_BPF_JIT bpf, testing: Add various tail call test cases bpf, x86: Emit patchable direct jump as tail call bpf: Constant map key tracking for prog array pokes bpf: Add poke dependency tracking for prog array maps bpf: Add initial poke descriptor table for jit images bpf: Move owner type, jited info into array auxiliary data ...
This commit is contained in:
Коммит
386403a115
|
@ -51,6 +51,14 @@ Description:
|
|||
packet processing. See the network driver for the exact
|
||||
meaning of this value.
|
||||
|
||||
What: /sys/class/<iface>/statistics/rx_errors
|
||||
Date: April 2005
|
||||
KernelVersion: 2.6.12
|
||||
Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
Indicates the number of receive errors on this network device.
|
||||
See the network driver for the exact meaning of this value.
|
||||
|
||||
What: /sys/class/<iface>/statistics/rx_fifo_errors
|
||||
Date: April 2005
|
||||
KernelVersion: 2.6.12
|
||||
|
@ -88,6 +96,14 @@ Description:
|
|||
due to lack of capacity in the receive side. See the network
|
||||
driver for the exact meaning of this value.
|
||||
|
||||
What: /sys/class/<iface>/statistics/rx_nohandler
|
||||
Date: February 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
Indicates the number of received packets that were dropped on
|
||||
an inactive device by the network core.
|
||||
|
||||
What: /sys/class/<iface>/statistics/rx_over_errors
|
||||
Date: April 2005
|
||||
KernelVersion: 2.6.12
|
||||
|
|
|
@ -47,6 +47,15 @@ Program types
|
|||
prog_flow_dissector
|
||||
|
||||
|
||||
Testing BPF
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
s390
|
||||
|
||||
|
||||
.. Links:
|
||||
.. _Documentation/networking/filter.txt: ../networking/filter.txt
|
||||
.. _man-pages: https://www.kernel.org/doc/man-pages/
|
||||
|
|
|
@ -142,3 +142,6 @@ BPF flow dissector doesn't support exporting all the metadata that in-kernel
|
|||
C-based implementation can export. Notable example is single VLAN (802.1Q)
|
||||
and double VLAN (802.1AD) tags. Please refer to the ``struct bpf_flow_keys``
|
||||
for a set of information that's currently can be exported from the BPF context.
|
||||
|
||||
When BPF flow dissector is attached to the root network namespace (machine-wide
|
||||
policy), users can't override it in their child network namespaces.
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
===================
|
||||
Testing BPF on s390
|
||||
===================
|
||||
|
||||
1. Introduction
|
||||
***************
|
||||
|
||||
IBM Z are mainframe computers, which are descendants of IBM System/360 from
|
||||
year 1964. They are supported by the Linux kernel under the name "s390". This
|
||||
document describes how to test BPF in an s390 QEMU guest.
|
||||
|
||||
2. One-time setup
|
||||
*****************
|
||||
|
||||
The following is required to build and run the test suite:
|
||||
|
||||
* s390 GCC
|
||||
* s390 development headers and libraries
|
||||
* Clang with BPF support
|
||||
* QEMU with s390 support
|
||||
* Disk image with s390 rootfs
|
||||
|
||||
Debian supports installing compiler and libraries for s390 out of the box.
|
||||
Users of other distros may use debootstrap in order to set up a Debian chroot::
|
||||
|
||||
sudo debootstrap \
|
||||
--variant=minbase \
|
||||
--include=sudo \
|
||||
testing \
|
||||
./s390-toolchain
|
||||
sudo mount --rbind /dev ./s390-toolchain/dev
|
||||
sudo mount --rbind /proc ./s390-toolchain/proc
|
||||
sudo mount --rbind /sys ./s390-toolchain/sys
|
||||
sudo chroot ./s390-toolchain
|
||||
|
||||
Once on Debian, the build prerequisites can be installed as follows::
|
||||
|
||||
sudo dpkg --add-architecture s390x
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
bc \
|
||||
bison \
|
||||
cmake \
|
||||
debootstrap \
|
||||
dwarves \
|
||||
flex \
|
||||
g++ \
|
||||
gcc \
|
||||
g++-s390x-linux-gnu \
|
||||
gcc-s390x-linux-gnu \
|
||||
gdb-multiarch \
|
||||
git \
|
||||
make \
|
||||
python3 \
|
||||
qemu-system-misc \
|
||||
qemu-utils \
|
||||
rsync \
|
||||
libcap-dev:s390x \
|
||||
libelf-dev:s390x \
|
||||
libncurses-dev
|
||||
|
||||
Latest Clang targeting BPF can be installed as follows::
|
||||
|
||||
git clone https://github.com/llvm/llvm-project.git
|
||||
ln -s ../../clang llvm-project/llvm/tools/
|
||||
mkdir llvm-project-build
|
||||
cd llvm-project-build
|
||||
cmake \
|
||||
-DLLVM_TARGETS_TO_BUILD=BPF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/clang-bpf \
|
||||
../llvm-project/llvm
|
||||
make
|
||||
sudo make install
|
||||
export PATH=/opt/clang-bpf/bin:$PATH
|
||||
|
||||
The disk image can be prepared using a loopback mount and debootstrap::
|
||||
|
||||
qemu-img create -f raw ./s390.img 1G
|
||||
sudo losetup -f ./s390.img
|
||||
sudo mkfs.ext4 /dev/loopX
|
||||
mkdir ./s390.rootfs
|
||||
sudo mount /dev/loopX ./s390.rootfs
|
||||
sudo debootstrap \
|
||||
--foreign \
|
||||
--arch=s390x \
|
||||
--variant=minbase \
|
||||
--include=" \
|
||||
iproute2, \
|
||||
iputils-ping, \
|
||||
isc-dhcp-client, \
|
||||
kmod, \
|
||||
libcap2, \
|
||||
libelf1, \
|
||||
netcat, \
|
||||
procps" \
|
||||
testing \
|
||||
./s390.rootfs
|
||||
sudo umount ./s390.rootfs
|
||||
sudo losetup -d /dev/loopX
|
||||
|
||||
3. Compilation
|
||||
**************
|
||||
|
||||
In addition to the usual Kconfig options required to run the BPF test suite, it
|
||||
is also helpful to select::
|
||||
|
||||
CONFIG_NET_9P=y
|
||||
CONFIG_9P_FS=y
|
||||
CONFIG_NET_9P_VIRTIO=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
|
||||
as that would enable a very easy way to share files with the s390 virtual
|
||||
machine.
|
||||
|
||||
Compiling kernel, modules and testsuite, as well as preparing gdb scripts to
|
||||
simplify debugging, can be done using the following commands::
|
||||
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- menuconfig
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- bzImage modules scripts_gdb
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- \
|
||||
-C tools/testing/selftests \
|
||||
TARGETS=bpf \
|
||||
INSTALL_PATH=$PWD/tools/testing/selftests/kselftest_install \
|
||||
install
|
||||
|
||||
4. Running the test suite
|
||||
*************************
|
||||
|
||||
The virtual machine can be started as follows::
|
||||
|
||||
qemu-system-s390x \
|
||||
-cpu max,zpci=on \
|
||||
-smp 2 \
|
||||
-m 4G \
|
||||
-kernel linux/arch/s390/boot/compressed/vmlinux \
|
||||
-drive file=./s390.img,if=virtio,format=raw \
|
||||
-nographic \
|
||||
-append 'root=/dev/vda rw console=ttyS1' \
|
||||
-virtfs local,path=./linux,security_model=none,mount_tag=linux \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0 \
|
||||
-device virtio-rng-ccw,rng=rng0 \
|
||||
-netdev user,id=net0 \
|
||||
-device virtio-net-ccw,netdev=net0
|
||||
|
||||
When using this on a real IBM Z, ``-enable-kvm`` may be added for better
|
||||
performance. When starting the virtual machine for the first time, disk image
|
||||
setup must be finalized using the following command::
|
||||
|
||||
/debootstrap/debootstrap --second-stage
|
||||
|
||||
Directory with the code built on the host as well as ``/proc`` and ``/sys``
|
||||
need to be mounted as follows::
|
||||
|
||||
mkdir -p /linux
|
||||
mount -t 9p linux /linux
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
|
||||
After that, the test suite can be run using the following commands::
|
||||
|
||||
cd /linux/tools/testing/selftests/kselftest_install
|
||||
./run_kselftest.sh
|
||||
|
||||
As usual, tests can be also run individually::
|
||||
|
||||
cd /linux/tools/testing/selftests/bpf
|
||||
./test_verifier
|
||||
|
||||
5. Debugging
|
||||
************
|
||||
|
||||
It is possible to debug the s390 kernel using QEMU GDB stub, which is activated
|
||||
by passing ``-s`` to QEMU.
|
||||
|
||||
It is preferable to turn KASLR off, so that gdb would know where to find the
|
||||
kernel image in memory, by building the kernel with::
|
||||
|
||||
RANDOMIZE_BASE=n
|
||||
|
||||
GDB can then be attached using the following command::
|
||||
|
||||
gdb-multiarch -ex 'target remote localhost:1234' ./vmlinux
|
||||
|
||||
6. Network
|
||||
**********
|
||||
|
||||
In case one needs to use the network in the virtual machine in order to e.g.
|
||||
install additional packages, it can be configured using::
|
||||
|
||||
dhclient eth0
|
||||
|
||||
7. Links
|
||||
********
|
||||
|
||||
This document is a compilation of techniques, whose more comprehensive
|
||||
descriptions can be found by following these links:
|
||||
|
||||
- `Debootstrap <https://wiki.debian.org/EmDebian/CrossDebootstrap>`_
|
||||
- `Multiarch <https://wiki.debian.org/Multiarch/HOWTO>`_
|
||||
- `Building LLVM <https://llvm.org/docs/CMake.html>`_
|
||||
- `Cross-compiling the kernel <https://wiki.gentoo.org/wiki/Embedded_Handbook/General/Cross-compiling_the_kernel>`_
|
||||
- `QEMU s390x Guest Support <https://wiki.qemu.org/Documentation/Platforms/S390X>`_
|
||||
- `Plan 9 folder sharing over Virtio <https://wiki.qemu.org/Documentation/9psetup>`_
|
||||
- `Using GDB with QEMU <https://wiki.osdev.org/Kernel_Debugging#Use_GDB_with_QEMU>`_
|
|
@ -44,6 +44,12 @@ Optional properties:
|
|||
Admission Control Block supports reporting the number of packets in-flight in a
|
||||
switch queue
|
||||
|
||||
- resets: a single phandle and reset identifier pair. See
|
||||
Documentation/devicetree/binding/reset/reset.txt for details.
|
||||
|
||||
- reset-names: If the "reset" property is specified, this property should have
|
||||
the value "switch" to denote the switch reset line.
|
||||
|
||||
Port subnodes:
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2",
|
||||
"brcm,genet-v3", "brcm,genet-v4", "brcm,genet-v5".
|
||||
"brcm,genet-v3", "brcm,genet-v4", "brcm,genet-v5", "brcm,bcm2711-genet-v5".
|
||||
- reg: address and length of the register set for the device
|
||||
- interrupts and/or interrupts-extended: must be two cells, the first cell
|
||||
is the general purpose interrupt line, while the second cell is the
|
||||
|
|
|
@ -14,6 +14,8 @@ Required properties:
|
|||
* "brcm,bcm4330-bt"
|
||||
* "brcm,bcm43438-bt"
|
||||
* "brcm,bcm4345c5"
|
||||
* "brcm,bcm43540-bt"
|
||||
* "brcm,bcm4335a0"
|
||||
|
||||
Optional properties:
|
||||
|
||||
|
|
|
@ -121,6 +121,11 @@ properties:
|
|||
and is useful for determining certain configuration settings
|
||||
such as flow control thresholds.
|
||||
|
||||
sfp:
|
||||
$ref: /schemas/types.yaml#definitions/phandle
|
||||
description:
|
||||
Specifies a reference to a node representing a SFP cage.
|
||||
|
||||
tx-fifo-depth:
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
description:
|
||||
|
|
|
@ -153,6 +153,11 @@ properties:
|
|||
Delay after the reset was deasserted in microseconds. If
|
||||
this property is missing the delay will be skipped.
|
||||
|
||||
sfp:
|
||||
$ref: /schemas/types.yaml#definitions/phandle
|
||||
description:
|
||||
Specifies a reference to a node representing a SFP cage.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
|
||||
- "aspeed,ast2400-mac"
|
||||
- "aspeed,ast2500-mac"
|
||||
- "aspeed,ast2600-mac"
|
||||
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain ethernet controller interrupt
|
||||
|
@ -23,6 +24,13 @@ Optional properties:
|
|||
- no-hw-checksum: Used to disable HW checksum support. Here for backward
|
||||
compatibility as the driver now should have correct defaults based on
|
||||
the SoC.
|
||||
- clocks: In accordance with the generic clock bindings. Must describe the MAC
|
||||
IP clock, and optionally an RMII RCLK gate for the AST2500/AST2600. The
|
||||
required MAC clock must be the first cell.
|
||||
- clock-names:
|
||||
|
||||
- "MACCLK": The MAC IP clock
|
||||
- "RCLK": Clock gate for the RMII RCLK
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@ Optional properties:
|
|||
absent, "rmii" is assumed.
|
||||
- use-iram: Use LPC32xx internal SRAM (IRAM) for DMA buffering
|
||||
|
||||
Optional subnodes:
|
||||
- mdio : specifies the mdio bus, used as a container for phy nodes according to
|
||||
phy.txt in the same directory
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
mac: ethernet@31060000 {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
* NXP Semiconductors PN532 NFC Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be
|
||||
- "nxp,pn532" Place a node with this inside the devicetree node of the bus
|
||||
where the NFC chip is connected to.
|
||||
Currently the kernel has phy bindings for uart and i2c.
|
||||
- "nxp,pn532-i2c" (DEPRECATED) only works for the i2c binding.
|
||||
- "nxp,pn533-i2c" (DEPRECATED) only works for the i2c binding.
|
||||
|
||||
Required properties if connected on i2c:
|
||||
- clock-frequency: I²C work frequency.
|
||||
- reg: for the I²C bus address. This is fixed at 0x24 for the PN532.
|
||||
- interrupts: GPIO interrupt to which the chip is connected
|
||||
|
||||
Optional SoC Specific Properties:
|
||||
- pinctrl-names: Contains only one value - "default".
|
||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||
|
||||
Example (for ARM-based BeagleBone with PN532 on I2C2):
|
||||
|
||||
&i2c2 {
|
||||
|
||||
|
||||
pn532: nfc@24 {
|
||||
|
||||
compatible = "nxp,pn532";
|
||||
|
||||
reg = <0x24>;
|
||||
clock-frequency = <400000>;
|
||||
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
Example (for PN532 connected via uart):
|
||||
|
||||
uart4: serial@49042000 {
|
||||
compatible = "ti,omap3-uart";
|
||||
|
||||
pn532: nfc {
|
||||
compatible = "nxp,pn532";
|
||||
};
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
* NXP Semiconductors PN532 NFC Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "nxp,pn532-i2c" or "nxp,pn533-i2c".
|
||||
- clock-frequency: I²C work frequency.
|
||||
- reg: address on the bus
|
||||
- interrupts: GPIO interrupt to which the chip is connected
|
||||
|
||||
Optional SoC Specific Properties:
|
||||
- pinctrl-names: Contains only one value - "default".
|
||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||
|
||||
Example (for ARM-based BeagleBone with PN532 on I2C2):
|
||||
|
||||
&i2c2 {
|
||||
|
||||
|
||||
pn532: pn532@24 {
|
||||
|
||||
compatible = "nxp,pn532-i2c";
|
||||
|
||||
reg = <0x24>;
|
||||
clock-frequency = <400000>;
|
||||
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
};
|
||||
};
|
|
@ -0,0 +1,111 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/qca,ar803x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Atheros AR803x PHY
|
||||
|
||||
maintainers:
|
||||
- Andrew Lunn <andrew@lunn.ch>
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
- Heiner Kallweit <hkallweit1@gmail.com>
|
||||
|
||||
description: |
|
||||
Bindings for Qualcomm Atheros AR803x PHYs
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-phy.yaml#
|
||||
|
||||
properties:
|
||||
qca,clk-out-frequency:
|
||||
description: Clock output frequency in Hertz.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [ 25000000, 50000000, 62500000, 125000000 ]
|
||||
|
||||
qca,clk-out-strength:
|
||||
description: Clock output driver strength.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [ 0, 1, 2 ]
|
||||
|
||||
qca,keep-pll-enabled:
|
||||
description: |
|
||||
If set, keep the PLL enabled even if there is no link. Useful if you
|
||||
want to use the clock output without an ethernet link.
|
||||
|
||||
Only supported on the AR8031.
|
||||
type: boolean
|
||||
|
||||
vddio-supply:
|
||||
description: |
|
||||
RGMII I/O voltage regulator (see regulator/regulator.yaml).
|
||||
|
||||
The PHY supports RGMII I/O voltages of 1.5V, 1.8V and 2.5V. You can
|
||||
either connect this to the vddio-regulator (1.5V / 1.8V) or the
|
||||
vddh-regulator (2.5V).
|
||||
|
||||
Only supported on the AR8031.
|
||||
|
||||
vddio-regulator:
|
||||
type: object
|
||||
description:
|
||||
Initial data for the VDDIO regulator. Set this to 1.5V or 1.8V.
|
||||
allOf:
|
||||
- $ref: /schemas/regulator/regulator.yaml
|
||||
|
||||
vddh-regulator:
|
||||
type: object
|
||||
description:
|
||||
Dummy subnode to model the external connection of the PHY VDDH
|
||||
regulator to VDDIO.
|
||||
allOf:
|
||||
- $ref: /schemas/regulator/regulator.yaml
|
||||
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/net/qca-ar803x.h>
|
||||
|
||||
ethernet {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy-mode = "rgmii-id";
|
||||
|
||||
ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
|
||||
qca,clk-out-frequency = <125000000>;
|
||||
qca,clk-out-strength = <AR803X_STRENGTH_FULL>;
|
||||
|
||||
vddio-supply = <&vddio>;
|
||||
|
||||
vddio: vddio-regulator {
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/net/qca-ar803x.h>
|
||||
|
||||
ethernet {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy-mode = "rgmii-id";
|
||||
|
||||
ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
|
||||
qca,clk-out-frequency = <50000000>;
|
||||
qca,keep-pll-enabled;
|
||||
|
||||
vddio-supply = <&vddh>;
|
||||
|
||||
vddh: vddh-regulator {
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/renesas,ether.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas Electronics SH EtherMAC
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-controller.yaml#
|
||||
|
||||
maintainers:
|
||||
- Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,gether-r8a7740 # device is a part of R8A7740 SoC
|
||||
- renesas,gether-r8a77980 # device is a part of R8A77980 SoC
|
||||
- renesas,ether-r7s72100 # device is a part of R7S72100 SoC
|
||||
- renesas,ether-r7s9210 # device is a part of R7S9210 SoC
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,ether-r8a7778 # device is a part of R8A7778 SoC
|
||||
- renesas,ether-r8a7779 # device is a part of R8A7779 SoC
|
||||
- enum:
|
||||
- renesas,rcar-gen1-ether # a generic R-Car Gen1 device
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,ether-r8a7745 # device is a part of R8A7745 SoC
|
||||
- renesas,ether-r8a7743 # device is a part of R8A7743 SoC
|
||||
- renesas,ether-r8a7790 # device is a part of R8A7790 SoC
|
||||
- renesas,ether-r8a7791 # device is a part of R8A7791 SoC
|
||||
- renesas,ether-r8a7793 # device is a part of R8A7793 SoC
|
||||
- renesas,ether-r8a7794 # device is a part of R8A7794 SoC
|
||||
- enum:
|
||||
- renesas,rcar-gen2-ether # a generic R-Car Gen2 or RZ/G1 device
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: E-DMAC/feLic registers
|
||||
- description: TSU registers
|
||||
minItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
'#address-cells':
|
||||
description: number of address cells for the MDIO bus
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
description: number of size cells on the MDIO bus
|
||||
const: 0
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
pinctrl-0: true
|
||||
|
||||
pinctrl-names: true
|
||||
|
||||
renesas,no-ether-link:
|
||||
type: boolean
|
||||
description:
|
||||
specify when a board does not provide a proper Ether LINK signal
|
||||
|
||||
renesas,ether-link-active-low:
|
||||
type: boolean
|
||||
description:
|
||||
specify when the Ether LINK signal is active-low instead of normal
|
||||
active-high
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- phy-mode
|
||||
- phy-handle
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
- clocks
|
||||
- pinctrl-0
|
||||
|
||||
examples:
|
||||
# Lager board
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a7790-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
ethernet@ee700000 {
|
||||
compatible = "renesas,ether-r8a7790", "renesas,rcar-gen2-ether";
|
||||
reg = <0 0xee700000 0 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
|
||||
phy-mode = "rmii";
|
||||
phy-handle = <&phy1>;
|
||||
pinctrl-0 = <ðer_pins>;
|
||||
pinctrl-names = "default";
|
||||
renesas,ether-link-active-low;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy1: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-0 = <&phy1_pins>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
* Renesas Electronics SH EtherMAC
|
||||
|
||||
This file provides information on what the device node for the SH EtherMAC
|
||||
interface contains.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain one or more of the following:
|
||||
"renesas,gether-r8a7740" if the device is a part of R8A7740 SoC.
|
||||
"renesas,ether-r8a7743" if the device is a part of R8A7743 SoC.
|
||||
"renesas,ether-r8a7745" if the device is a part of R8A7745 SoC.
|
||||
"renesas,ether-r8a7778" if the device is a part of R8A7778 SoC.
|
||||
"renesas,ether-r8a7779" if the device is a part of R8A7779 SoC.
|
||||
"renesas,ether-r8a7790" if the device is a part of R8A7790 SoC.
|
||||
"renesas,ether-r8a7791" if the device is a part of R8A7791 SoC.
|
||||
"renesas,ether-r8a7793" if the device is a part of R8A7793 SoC.
|
||||
"renesas,ether-r8a7794" if the device is a part of R8A7794 SoC.
|
||||
"renesas,gether-r8a77980" if the device is a part of R8A77980 SoC.
|
||||
"renesas,ether-r7s72100" if the device is a part of R7S72100 SoC.
|
||||
"renesas,ether-r7s9210" if the device is a part of R7S9210 SoC.
|
||||
"renesas,rcar-gen1-ether" for a generic R-Car Gen1 device.
|
||||
"renesas,rcar-gen2-ether" for a generic R-Car Gen2 or RZ/G1
|
||||
device.
|
||||
|
||||
When compatible with the generic version, nodes must list
|
||||
the SoC-specific version corresponding to the platform
|
||||
first followed by the generic version.
|
||||
|
||||
- reg: offset and length of (1) the E-DMAC/feLic register block (required),
|
||||
(2) the TSU register block (optional).
|
||||
- interrupts: interrupt specifier for the sole interrupt.
|
||||
- phy-mode: see ethernet.txt file in the same directory.
|
||||
- phy-handle: see ethernet.txt file in the same directory.
|
||||
- #address-cells: number of address cells for the MDIO bus, must be equal to 1.
|
||||
- #size-cells: number of size cells on the MDIO bus, must be equal to 0.
|
||||
- clocks: clock phandle and specifier pair.
|
||||
- pinctrl-0: phandle, referring to a default pin configuration node.
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names: pin configuration state name ("default").
|
||||
- renesas,no-ether-link: boolean, specify when a board does not provide a proper
|
||||
Ether LINK signal.
|
||||
- renesas,ether-link-active-low: boolean, specify when the Ether LINK signal is
|
||||
active-low instead of normal active-high.
|
||||
|
||||
Example (Lager board):
|
||||
|
||||
ethernet@ee700000 {
|
||||
compatible = "renesas,ether-r8a7790",
|
||||
"renesas,rcar-gen2-ether";
|
||||
reg = <0 0xee700000 0 0x400>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 162 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp8_clks R8A7790_CLK_ETHER>;
|
||||
phy-mode = "rmii";
|
||||
phy-handle = <&phy1>;
|
||||
pinctrl-0 = <ðer_pins>;
|
||||
pinctrl-names = "default";
|
||||
renesas,ether-link-active-low;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy1: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
interrupt-parent = <&irqc0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-0 = <&phy1_pins>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,240 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/ti,cpsw-switch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI SoC Ethernet Switch Controller (CPSW) Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
- Sekhar Nori <nsekhar@ti.com>
|
||||
|
||||
description:
|
||||
The 3-port switch gigabit ethernet subsystem provides ethernet packet
|
||||
communication and can be configured as an ethernet switch. It provides the
|
||||
gigabit media independent interface (GMII),reduced gigabit media
|
||||
independent interface (RGMII), reduced media independent interface (RMII),
|
||||
the management data input output (MDIO) for physical layer device (PHY)
|
||||
management.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: ti,cpsw-switch
|
||||
- items:
|
||||
- const: ti,am335x-cpsw-switch
|
||||
- const: ti,cpsw-switch
|
||||
- items:
|
||||
- const: ti,am4372-cpsw-switch
|
||||
- const: ti,cpsw-switch
|
||||
- items:
|
||||
- const: ti,dra7-cpsw-switch
|
||||
- const: ti,cpsw-switch
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description:
|
||||
The physical base address and size of full the CPSW module IO range
|
||||
|
||||
ranges: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: CPSW functional clock
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
items:
|
||||
- const: fck
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: RX_THRESH interrupt
|
||||
- description: RX interrupt
|
||||
- description: TX interrupt
|
||||
- description: MISC interrupt
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: "rx_thresh"
|
||||
- const: "rx"
|
||||
- const: "tx"
|
||||
- const: "misc"
|
||||
|
||||
pinctrl-names: true
|
||||
|
||||
syscon:
|
||||
$ref: /schemas/types.yaml#definitions/phandle
|
||||
description:
|
||||
Phandle to the system control device node which provides access to
|
||||
efuse IO range with MAC addresses
|
||||
|
||||
|
||||
ethernet-ports:
|
||||
type: object
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^port@[0-9]+$":
|
||||
type: object
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description: CPSW external ports
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-controller.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
enum: [1, 2]
|
||||
description: CPSW port number
|
||||
|
||||
phys:
|
||||
$ref: /schemas/types.yaml#definitions/phandle-array
|
||||
maxItems: 1
|
||||
description: phandle on phy-gmii-sel PHY
|
||||
|
||||
label:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
maxItems: 1
|
||||
description: label associated with this port
|
||||
|
||||
ti,dual-emac-pvid:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maxItems: 1
|
||||
minimum: 1
|
||||
maximum: 1024
|
||||
description:
|
||||
Specifies default PORT VID to be used to segregate
|
||||
ports. Default value - CPSW port number.
|
||||
|
||||
required:
|
||||
- reg
|
||||
- phys
|
||||
|
||||
mdio:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: "ti,davinci-mdio.yaml#"
|
||||
description:
|
||||
CPSW MDIO bus.
|
||||
|
||||
cpts:
|
||||
type: object
|
||||
description:
|
||||
The Common Platform Time Sync (CPTS) module
|
||||
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: CPTS reference clock
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
items:
|
||||
- const: cpts
|
||||
|
||||
cpts_clock_mult:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Numerator to convert input clock ticks into ns
|
||||
|
||||
cpts_clock_shift:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Denominator to convert input clock ticks into ns.
|
||||
Mult and shift will be calculated basing on CPTS rftclk frequency if
|
||||
both cpts_clock_shift and cpts_clock_mult properties are not provided.
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- ranges
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/dra7.h>
|
||||
|
||||
mac_sw: switch@0 {
|
||||
compatible = "ti,dra7-cpsw-switch","ti,cpsw-switch";
|
||||
reg = <0x0 0x4000>;
|
||||
ranges = <0 0 0x4000>;
|
||||
clocks = <&gmac_main_clk>;
|
||||
clock-names = "fck";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
syscon = <&scm_conf>;
|
||||
inctrl-names = "default", "sleep";
|
||||
|
||||
interrupts = <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "rx_thresh", "rx", "tx", "misc";
|
||||
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpsw_port1: port@1 {
|
||||
reg = <1>;
|
||||
label = "port1";
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1>;
|
||||
phy-handle = <ðphy0_sw>;
|
||||
phy-mode = "rgmii";
|
||||
ti,dual_emac_pvid = <1>;
|
||||
};
|
||||
|
||||
cpsw_port2: port@2 {
|
||||
reg = <2>;
|
||||
label = "wan";
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2>;
|
||||
phy-handle = <ðphy1_sw>;
|
||||
phy-mode = "rgmii";
|
||||
ti,dual_emac_pvid = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
davinci_mdio_sw: mdio@1000 {
|
||||
compatible = "ti,cpsw-mdio","ti,davinci_mdio";
|
||||
reg = <0x1000 0x100>;
|
||||
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 0>;
|
||||
clock-names = "fck";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
bus_freq = <1000000>;
|
||||
|
||||
ethphy0_sw: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
ethphy1_sw: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
cpts {
|
||||
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 25>;
|
||||
clock-names = "cpts";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,84 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2019 Texas Instruments Incorporated
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/net/ti,dp83869.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: TI DP83869 ethernet PHY
|
||||
|
||||
allOf:
|
||||
- $ref: "ethernet-controller.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Dan Murphy <dmurphy@ti.com>
|
||||
|
||||
description: |
|
||||
The DP83869HM device is a robust, fully-featured Gigabit (PHY) transceiver
|
||||
with integrated PMD sublayers that supports 10BASE-Te, 100BASE-TX and
|
||||
1000BASE-T Ethernet protocols. The DP83869 also supports 1000BASE-X and
|
||||
100BASE-FX Fiber protocols.
|
||||
This device interfaces to the MAC layer through Reduced GMII (RGMII) and
|
||||
SGMII The DP83869HM supports Media Conversion in Managed mode. In this mode,
|
||||
the DP83869HM can run 1000BASE-X-to-1000BASE-T and 100BASE-FX-to-100BASE-TX
|
||||
conversions. The DP83869HM can also support Bridge Conversion from RGMII to
|
||||
SGMII and SGMII to RGMII.
|
||||
|
||||
Specifications about the charger can be found at:
|
||||
http://www.ti.com/lit/ds/symlink/dp83869hm.pdf
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
ti,min-output-impedance:
|
||||
type: boolean
|
||||
description: |
|
||||
MAC Interface Impedance control to set the programmable output impedance
|
||||
to a minimum value (35 ohms).
|
||||
|
||||
ti,max-output-impedance:
|
||||
type: boolean
|
||||
description: |
|
||||
MAC Interface Impedance control to set the programmable output impedance
|
||||
to a maximum value (70 ohms).
|
||||
|
||||
tx-fifo-depth:
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
description: |
|
||||
Transmitt FIFO depth see dt-bindings/net/ti-dp83869.h for values
|
||||
|
||||
rx-fifo-depth:
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
description: |
|
||||
Receive FIFO depth see dt-bindings/net/ti-dp83869.h for values
|
||||
|
||||
ti,clk-output-sel:
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
description: |
|
||||
Muxing option for CLK_OUT pin see dt-bindings/net/ti-dp83869.h for values.
|
||||
|
||||
ti,op-mode:
|
||||
$ref: /schemas/types.yaml#definitions/uint32
|
||||
description: |
|
||||
Operational mode for the PHY. If this is not set then the operational
|
||||
mode is set by the straps. see dt-bindings/net/ti-dp83869.h for values
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/net/ti-dp83869.h>
|
||||
mdio0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
ethphy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
tx-fifo-depth = <DP83869_PHYCR_FIFO_DEPTH_4_B_NIB>;
|
||||
rx-fifo-depth = <DP83869_PHYCR_FIFO_DEPTH_4_B_NIB>;
|
||||
ti,op-mode = <DP83869_RGMII_COPPER_ETHERNET>;
|
||||
ti,max-output-impedance = "true";
|
||||
ti,clk-output-sel = <DP83869_CLK_O_SEL_CHN_A_RCLK>;
|
||||
};
|
||||
};
|
|
@ -81,6 +81,12 @@ Optional properties:
|
|||
Definition: Name of external front end module used. Some valid FEM names
|
||||
for example: "microsemi-lx5586", "sky85703-11"
|
||||
and "sky85803" etc.
|
||||
- qcom,snoc-host-cap-8bit-quirk:
|
||||
Usage: Optional
|
||||
Value type: <empty>
|
||||
Definition: Quirk specifying that the firmware expects the 8bit version
|
||||
of the host capability QMI request
|
||||
- qcom,xo-cal-data: xo cal offset to be configured in xo trim register.
|
||||
|
||||
Example (to supply PCI based wifi block details):
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/ptp/ptp-idtcm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: IDT ClockMatrix (TM) PTP Clock Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Vincent Cheng <vincent.cheng.xh@renesas.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
# For System Synchronizer
|
||||
- idt,8a34000
|
||||
- idt,8a34001
|
||||
- idt,8a34002
|
||||
- idt,8a34003
|
||||
- idt,8a34004
|
||||
- idt,8a34005
|
||||
- idt,8a34006
|
||||
- idt,8a34007
|
||||
- idt,8a34008
|
||||
- idt,8a34009
|
||||
# For Port Synchronizer
|
||||
- idt,8a34010
|
||||
- idt,8a34011
|
||||
- idt,8a34012
|
||||
- idt,8a34013
|
||||
- idt,8a34014
|
||||
- idt,8a34015
|
||||
- idt,8a34016
|
||||
- idt,8a34017
|
||||
- idt,8a34018
|
||||
- idt,8a34019
|
||||
# For Universal Frequency Translator (UFT)
|
||||
- idt,8a34040
|
||||
- idt,8a34041
|
||||
- idt,8a34042
|
||||
- idt,8a34043
|
||||
- idt,8a34044
|
||||
- idt,8a34045
|
||||
- idt,8a34046
|
||||
- idt,8a34047
|
||||
- idt,8a34048
|
||||
- idt,8a34049
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description:
|
||||
I2C slave address of the device.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@1 {
|
||||
compatible = "abc,acme-1234";
|
||||
reg = <0x01 0x400>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phc@5b {
|
||||
compatible = "idt,8a34000";
|
||||
reg = <0x5b>;
|
||||
};
|
||||
};
|
|
@ -40,13 +40,13 @@ allocates memory for this UMEM using whatever means it feels is most
|
|||
appropriate (malloc, mmap, huge pages, etc). This memory area is then
|
||||
registered with the kernel using the new setsockopt XDP_UMEM_REG. The
|
||||
UMEM also has two rings: the FILL ring and the COMPLETION ring. The
|
||||
fill ring is used by the application to send down addr for the kernel
|
||||
FILL ring is used by the application to send down addr for the kernel
|
||||
to fill in with RX packet data. References to these frames will then
|
||||
appear in the RX ring once each packet has been received. The
|
||||
completion ring, on the other hand, contains frame addr that the
|
||||
COMPLETION ring, on the other hand, contains frame addr that the
|
||||
kernel has transmitted completely and can now be used again by user
|
||||
space, for either TX or RX. Thus, the frame addrs appearing in the
|
||||
completion ring are addrs that were previously transmitted using the
|
||||
COMPLETION ring are addrs that were previously transmitted using the
|
||||
TX ring. In summary, the RX and FILL rings are used for the RX path
|
||||
and the TX and COMPLETION rings are used for the TX path.
|
||||
|
||||
|
@ -91,11 +91,16 @@ Concepts
|
|||
========
|
||||
|
||||
In order to use an AF_XDP socket, a number of associated objects need
|
||||
to be setup.
|
||||
to be setup. These objects and their options are explained in the
|
||||
following sections.
|
||||
|
||||
Jonathan Corbet has also written an excellent article on LWN,
|
||||
"Accelerating networking with AF_XDP". It can be found at
|
||||
https://lwn.net/Articles/750845/.
|
||||
For an overview on how AF_XDP works, you can also take a look at the
|
||||
Linux Plumbers paper from 2018 on the subject:
|
||||
http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf. Do
|
||||
NOT consult the paper from 2017 on "AF_PACKET v4", the first attempt
|
||||
at AF_XDP. Nearly everything changed since then. Jonathan Corbet has
|
||||
also written an excellent article on LWN, "Accelerating networking
|
||||
with AF_XDP". It can be found at https://lwn.net/Articles/750845/.
|
||||
|
||||
UMEM
|
||||
----
|
||||
|
@ -113,22 +118,22 @@ the next socket B can do this by setting the XDP_SHARED_UMEM flag in
|
|||
struct sockaddr_xdp member sxdp_flags, and passing the file descriptor
|
||||
of A to struct sockaddr_xdp member sxdp_shared_umem_fd.
|
||||
|
||||
The UMEM has two single-producer/single-consumer rings, that are used
|
||||
The UMEM has two single-producer/single-consumer rings that are used
|
||||
to transfer ownership of UMEM frames between the kernel and the
|
||||
user-space application.
|
||||
|
||||
Rings
|
||||
-----
|
||||
|
||||
There are a four different kind of rings: Fill, Completion, RX and
|
||||
There are a four different kind of rings: FILL, COMPLETION, RX and
|
||||
TX. All rings are single-producer/single-consumer, so the user-space
|
||||
application need explicit synchronization of multiple
|
||||
processes/threads are reading/writing to them.
|
||||
|
||||
The UMEM uses two rings: Fill and Completion. Each socket associated
|
||||
The UMEM uses two rings: FILL and COMPLETION. Each socket associated
|
||||
with the UMEM must have an RX queue, TX queue or both. Say, that there
|
||||
is a setup with four sockets (all doing TX and RX). Then there will be
|
||||
one Fill ring, one Completion ring, four TX rings and four RX rings.
|
||||
one FILL ring, one COMPLETION ring, four TX rings and four RX rings.
|
||||
|
||||
The rings are head(producer)/tail(consumer) based rings. A producer
|
||||
writes the data ring at the index pointed out by struct xdp_ring
|
||||
|
@ -146,7 +151,7 @@ The size of the rings need to be of size power of two.
|
|||
UMEM Fill Ring
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The Fill ring is used to transfer ownership of UMEM frames from
|
||||
The FILL ring is used to transfer ownership of UMEM frames from
|
||||
user-space to kernel-space. The UMEM addrs are passed in the ring. As
|
||||
an example, if the UMEM is 64k and each chunk is 4k, then the UMEM has
|
||||
16 chunks and can pass addrs between 0 and 64k.
|
||||
|
@ -164,8 +169,8 @@ chunks mode, then the incoming addr will be left untouched.
|
|||
UMEM Completion Ring
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Completion Ring is used transfer ownership of UMEM frames from
|
||||
kernel-space to user-space. Just like the Fill ring, UMEM indicies are
|
||||
The COMPLETION Ring is used transfer ownership of UMEM frames from
|
||||
kernel-space to user-space. Just like the FILL ring, UMEM indices are
|
||||
used.
|
||||
|
||||
Frames passed from the kernel to user-space are frames that has been
|
||||
|
@ -181,7 +186,7 @@ The RX ring is the receiving side of a socket. Each entry in the ring
|
|||
is a struct xdp_desc descriptor. The descriptor contains UMEM offset
|
||||
(addr) and the length of the data (len).
|
||||
|
||||
If no frames have been passed to kernel via the Fill ring, no
|
||||
If no frames have been passed to kernel via the FILL ring, no
|
||||
descriptors will (or can) appear on the RX ring.
|
||||
|
||||
The user application consumes struct xdp_desc descriptors from this
|
||||
|
@ -199,8 +204,24 @@ be relaxed in the future.
|
|||
The user application produces struct xdp_desc descriptors to this
|
||||
ring.
|
||||
|
||||
Libbpf
|
||||
======
|
||||
|
||||
Libbpf is a helper library for eBPF and XDP that makes using these
|
||||
technologies a lot simpler. It also contains specific helper functions
|
||||
in tools/lib/bpf/xsk.h for facilitating the use of AF_XDP. It
|
||||
contains two types of functions: those that can be used to make the
|
||||
setup of AF_XDP socket easier and ones that can be used in the data
|
||||
plane to access the rings safely and quickly. To see an example on how
|
||||
to use this API, please take a look at the sample application in
|
||||
samples/bpf/xdpsock_usr.c which uses libbpf for both setup and data
|
||||
plane operations.
|
||||
|
||||
We recommend that you use this library unless you have become a power
|
||||
user. It will make your program a lot simpler.
|
||||
|
||||
XSKMAP / BPF_MAP_TYPE_XSKMAP
|
||||
----------------------------
|
||||
============================
|
||||
|
||||
On XDP side there is a BPF map type BPF_MAP_TYPE_XSKMAP (XSKMAP) that
|
||||
is used in conjunction with bpf_redirect_map() to pass the ingress
|
||||
|
@ -216,21 +237,202 @@ queue 17. Only the XDP program executing for eth0 and queue 17 will
|
|||
successfully pass data to the socket. Please refer to the sample
|
||||
application (samples/bpf/) in for an example.
|
||||
|
||||
Configuration Flags and Socket Options
|
||||
======================================
|
||||
|
||||
These are the various configuration flags that can be used to control
|
||||
and monitor the behavior of AF_XDP sockets.
|
||||
|
||||
XDP_COPY and XDP_ZERO_COPY bind flags
|
||||
-------------------------------------
|
||||
|
||||
When you bind to a socket, the kernel will first try to use zero-copy
|
||||
copy. If zero-copy is not supported, it will fall back on using copy
|
||||
mode, i.e. copying all packets out to user space. But if you would
|
||||
like to force a certain mode, you can use the following flags. If you
|
||||
pass the XDP_COPY flag to the bind call, the kernel will force the
|
||||
socket into copy mode. If it cannot use copy mode, the bind call will
|
||||
fail with an error. Conversely, the XDP_ZERO_COPY flag will force the
|
||||
socket into zero-copy mode or fail.
|
||||
|
||||
XDP_SHARED_UMEM bind flag
|
||||
-------------------------
|
||||
|
||||
This flag enables you to bind multiple sockets to the same UMEM, but
|
||||
only if they share the same queue id. In this mode, each socket has
|
||||
their own RX and TX rings, but the UMEM (tied to the fist socket
|
||||
created) only has a single FILL ring and a single COMPLETION
|
||||
ring. To use this mode, create the first socket and bind it in the normal
|
||||
way. Create a second socket and create an RX and a TX ring, or at
|
||||
least one of them, but no FILL or COMPLETION rings as the ones from
|
||||
the first socket will be used. In the bind call, set he
|
||||
XDP_SHARED_UMEM option and provide the initial socket's fd in the
|
||||
sxdp_shared_umem_fd field. You can attach an arbitrary number of extra
|
||||
sockets this way.
|
||||
|
||||
What socket will then a packet arrive on? This is decided by the XDP
|
||||
program. Put all the sockets in the XSK_MAP and just indicate which
|
||||
index in the array you would like to send each packet to. A simple
|
||||
round-robin example of distributing packets is shown below:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define MAX_SOCKS 16
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_XSKMAP);
|
||||
__uint(max_entries, MAX_SOCKS);
|
||||
__uint(key_size, sizeof(int));
|
||||
__uint(value_size, sizeof(int));
|
||||
} xsks_map SEC(".maps");
|
||||
|
||||
static unsigned int rr;
|
||||
|
||||
SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
|
||||
{
|
||||
rr = (rr + 1) & (MAX_SOCKS - 1);
|
||||
|
||||
return bpf_redirect_map(&xsks_map, rr, XDP_DROP);
|
||||
}
|
||||
|
||||
Note, that since there is only a single set of FILL and COMPLETION
|
||||
rings, and they are single producer, single consumer rings, you need
|
||||
to make sure that multiple processes or threads do not use these rings
|
||||
concurrently. There are no synchronization primitives in the
|
||||
libbpf code that protects multiple users at this point in time.
|
||||
|
||||
Libbpf uses this mode if you create more than one socket tied to the
|
||||
same umem. However, note that you need to supply the
|
||||
XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD libbpf_flag with the
|
||||
xsk_socket__create calls and load your own XDP program as there is no
|
||||
built in one in libbpf that will route the traffic for you.
|
||||
|
||||
XDP_USE_NEED_WAKEUP bind flag
|
||||
-----------------------------
|
||||
|
||||
This option adds support for a new flag called need_wakeup that is
|
||||
present in the FILL ring and the TX ring, the rings for which user
|
||||
space is a producer. When this option is set in the bind call, the
|
||||
need_wakeup flag will be set if the kernel needs to be explicitly
|
||||
woken up by a syscall to continue processing packets. If the flag is
|
||||
zero, no syscall is needed.
|
||||
|
||||
If the flag is set on the FILL ring, the application needs to call
|
||||
poll() to be able to continue to receive packets on the RX ring. This
|
||||
can happen, for example, when the kernel has detected that there are no
|
||||
more buffers on the FILL ring and no buffers left on the RX HW ring of
|
||||
the NIC. In this case, interrupts are turned off as the NIC cannot
|
||||
receive any packets (as there are no buffers to put them in), and the
|
||||
need_wakeup flag is set so that user space can put buffers on the
|
||||
FILL ring and then call poll() so that the kernel driver can put these
|
||||
buffers on the HW ring and start to receive packets.
|
||||
|
||||
If the flag is set for the TX ring, it means that the application
|
||||
needs to explicitly notify the kernel to send any packets put on the
|
||||
TX ring. This can be accomplished either by a poll() call, as in the
|
||||
RX path, or by calling sendto().
|
||||
|
||||
An example of how to use this flag can be found in
|
||||
samples/bpf/xdpsock_user.c. An example with the use of libbpf helpers
|
||||
would look like this for the TX path:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if (xsk_ring_prod__needs_wakeup(&my_tx_ring))
|
||||
sendto(xsk_socket__fd(xsk_handle), NULL, 0, MSG_DONTWAIT, NULL, 0);
|
||||
|
||||
I.e., only use the syscall if the flag is set.
|
||||
|
||||
We recommend that you always enable this mode as it usually leads to
|
||||
better performance especially if you run the application and the
|
||||
driver on the same core, but also if you use different cores for the
|
||||
application and the kernel driver, as it reduces the number of
|
||||
syscalls needed for the TX path.
|
||||
|
||||
XDP_{RX|TX|UMEM_FILL|UMEM_COMPLETION}_RING setsockopts
|
||||
------------------------------------------------------
|
||||
|
||||
These setsockopts sets the number of descriptors that the RX, TX,
|
||||
FILL, and COMPLETION rings respectively should have. It is mandatory
|
||||
to set the size of at least one of the RX and TX rings. If you set
|
||||
both, you will be able to both receive and send traffic from your
|
||||
application, but if you only want to do one of them, you can save
|
||||
resources by only setting up one of them. Both the FILL ring and the
|
||||
COMPLETION ring are mandatory as you need to have a UMEM tied to your
|
||||
socket. But if the XDP_SHARED_UMEM flag is used, any socket after the
|
||||
first one does not have a UMEM and should in that case not have any
|
||||
FILL or COMPLETION rings created as the ones from the shared umem will
|
||||
be used. Note, that the rings are single-producer single-consumer, so
|
||||
do not try to access them from multiple processes at the same
|
||||
time. See the XDP_SHARED_UMEM section.
|
||||
|
||||
In libbpf, you can create Rx-only and Tx-only sockets by supplying
|
||||
NULL to the rx and tx arguments, respectively, to the
|
||||
xsk_socket__create function.
|
||||
|
||||
If you create a Tx-only socket, we recommend that you do not put any
|
||||
packets on the fill ring. If you do this, drivers might think you are
|
||||
going to receive something when you in fact will not, and this can
|
||||
negatively impact performance.
|
||||
|
||||
XDP_UMEM_REG setsockopt
|
||||
-----------------------
|
||||
|
||||
This setsockopt registers a UMEM to a socket. This is the area that
|
||||
contain all the buffers that packet can recide in. The call takes a
|
||||
pointer to the beginning of this area and the size of it. Moreover, it
|
||||
also has parameter called chunk_size that is the size that the UMEM is
|
||||
divided into. It can only be 2K or 4K at the moment. If you have an
|
||||
UMEM area that is 128K and a chunk size of 2K, this means that you
|
||||
will be able to hold a maximum of 128K / 2K = 64 packets in your UMEM
|
||||
area and that your largest packet size can be 2K.
|
||||
|
||||
There is also an option to set the headroom of each single buffer in
|
||||
the UMEM. If you set this to N bytes, it means that the packet will
|
||||
start N bytes into the buffer leaving the first N bytes for the
|
||||
application to use. The final option is the flags field, but it will
|
||||
be dealt with in separate sections for each UMEM flag.
|
||||
|
||||
XDP_STATISTICS getsockopt
|
||||
-------------------------
|
||||
|
||||
Gets drop statistics of a socket that can be useful for debug
|
||||
purposes. The supported statistics are shown below:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct xdp_statistics {
|
||||
__u64 rx_dropped; /* Dropped for reasons other than invalid desc */
|
||||
__u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
|
||||
__u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
|
||||
};
|
||||
|
||||
XDP_OPTIONS getsockopt
|
||||
----------------------
|
||||
|
||||
Gets options from an XDP socket. The only one supported so far is
|
||||
XDP_OPTIONS_ZEROCOPY which tells you if zero-copy is on or not.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
In order to use AF_XDP sockets there are two parts needed. The
|
||||
In order to use AF_XDP sockets two parts are needed. The
|
||||
user-space application and the XDP program. For a complete setup and
|
||||
usage example, please refer to the sample application. The user-space
|
||||
side is xdpsock_user.c and the XDP side is part of libbpf.
|
||||
|
||||
The XDP code sample included in tools/lib/bpf/xsk.c is the following::
|
||||
The XDP code sample included in tools/lib/bpf/xsk.c is the following:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
|
||||
{
|
||||
int index = ctx->rx_queue_index;
|
||||
|
||||
// A set entry here means that the correspnding queue_id
|
||||
// A set entry here means that the corresponding queue_id
|
||||
// has an active AF_XDP socket bound to it.
|
||||
if (bpf_map_lookup_elem(&xsks_map, &index))
|
||||
return bpf_redirect_map(&xsks_map, index, 0);
|
||||
|
@ -238,7 +440,10 @@ The XDP code sample included in tools/lib/bpf/xsk.c is the following::
|
|||
return XDP_PASS;
|
||||
}
|
||||
|
||||
Naive ring dequeue and enqueue could look like this::
|
||||
A simple but not so performance ring dequeue and enqueue could look
|
||||
like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// struct xdp_rxtx_ring {
|
||||
// __u32 *producer;
|
||||
|
@ -287,17 +492,16 @@ Naive ring dequeue and enqueue could look like this::
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
For a more optimized version, please refer to the sample application.
|
||||
But please use the libbpf functions as they are optimized and ready to
|
||||
use. Will make your life easier.
|
||||
|
||||
Sample application
|
||||
==================
|
||||
|
||||
There is a xdpsock benchmarking/test application included that
|
||||
demonstrates how to use AF_XDP sockets with both private and shared
|
||||
UMEMs. Say that you would like your UDP traffic from port 4242 to end
|
||||
up in queue 16, that we will enable AF_XDP on. Here, we use ethtool
|
||||
for this::
|
||||
demonstrates how to use AF_XDP sockets with private UMEMs. Say that
|
||||
you would like your UDP traffic from port 4242 to end up in queue 16,
|
||||
that we will enable AF_XDP on. Here, we use ethtool for this::
|
||||
|
||||
ethtool -N p3p2 rx-flow-hash udp4 fn
|
||||
ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \
|
||||
|
@ -311,13 +515,18 @@ using::
|
|||
For XDP_SKB mode, use the switch "-S" instead of "-N" and all options
|
||||
can be displayed with "-h", as usual.
|
||||
|
||||
This sample application uses libbpf to make the setup and usage of
|
||||
AF_XDP simpler. If you want to know how the raw uapi of AF_XDP is
|
||||
really used to make something more advanced, take a look at the libbpf
|
||||
code in tools/lib/bpf/xsk.[ch].
|
||||
|
||||
FAQ
|
||||
=======
|
||||
|
||||
Q: I am not seeing any traffic on the socket. What am I doing wrong?
|
||||
|
||||
A: When a netdev of a physical NIC is initialized, Linux usually
|
||||
allocates one Rx and Tx queue pair per core. So on a 8 core system,
|
||||
allocates one RX and TX queue pair per core. So on a 8 core system,
|
||||
queue ids 0 to 7 will be allocated, one per core. In the AF_XDP
|
||||
bind call or the xsk_socket__create libbpf function call, you
|
||||
specify a specific queue id to bind to and it is only the traffic
|
||||
|
@ -343,9 +552,21 @@ A: When a netdev of a physical NIC is initialized, Linux usually
|
|||
sudo ethtool -N <interface> flow-type udp4 src-port 4242 dst-port \
|
||||
4242 action 2
|
||||
|
||||
A number of other ways are possible all up to the capabilitites of
|
||||
A number of other ways are possible all up to the capabilities of
|
||||
the NIC you have.
|
||||
|
||||
Q: Can I use the XSKMAP to implement a switch betwen different umems
|
||||
in copy mode?
|
||||
|
||||
A: The short answer is no, that is not supported at the moment. The
|
||||
XSKMAP can only be used to switch traffic coming in on queue id X
|
||||
to sockets bound to the same queue id X. The XSKMAP can contain
|
||||
sockets bound to different queue ids, for example X and Y, but only
|
||||
traffic goming in from queue id Y can be directed to sockets bound
|
||||
to the same queue id Y. In zero-copy mode, you should use the
|
||||
switch, or other distribution mechanism, in your NIC to direct
|
||||
traffic to the correct queue id and socket.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
aQuantia AQtion Driver for the aQuantia Multi-Gigabit PCI Express Family of
|
||||
Ethernet Adapters
|
||||
Marvell(Aquantia) AQtion Driver for the aQuantia Multi-Gigabit PCI Express
|
||||
Family of Ethernet Adapters
|
||||
=============================================================================
|
||||
|
||||
Contents
|
||||
|
@ -325,6 +325,46 @@ Supported ethtool options
|
|||
Example:
|
||||
ethtool -N eth0 flow-type udp4 action 0 loc 32
|
||||
|
||||
UDP GSO hardware offload
|
||||
---------------------------------
|
||||
UDP GSO allows to boost UDP tx rates by offloading UDP headers allocation
|
||||
into hardware. A special userspace socket option is required for this,
|
||||
could be validated with /kernel/tools/testing/selftests/net/
|
||||
|
||||
udpgso_bench_tx -u -4 -D 10.0.1.1 -s 6300 -S 100
|
||||
|
||||
Will cause sending out of 100 byte sized UDP packets formed from single
|
||||
6300 bytes user buffer.
|
||||
|
||||
UDP GSO is configured by:
|
||||
|
||||
ethtool -K eth0 tx-udp-segmentation on
|
||||
|
||||
Private flags (testing)
|
||||
---------------------------------
|
||||
|
||||
Atlantic driver supports private flags for hardware custom features:
|
||||
|
||||
$ ethtool --show-priv-flags ethX
|
||||
|
||||
Private flags for ethX:
|
||||
DMASystemLoopback : off
|
||||
PKTSystemLoopback : off
|
||||
DMANetworkLoopback : off
|
||||
PHYInternalLoopback: off
|
||||
PHYExternalLoopback: off
|
||||
|
||||
Example:
|
||||
|
||||
$ ethtool --set-priv-flags ethX DMASystemLoopback on
|
||||
|
||||
DMASystemLoopback: DMA Host loopback.
|
||||
PKTSystemLoopback: Packet buffer host loopback.
|
||||
DMANetworkLoopback: Network side loopback on DMA block.
|
||||
PHYInternalLoopback: Internal loopback on Phy.
|
||||
PHYExternalLoopback: External loopback on Phy (with loopback ethernet cable).
|
||||
|
||||
|
||||
Command Line Parameters
|
||||
=======================
|
||||
The following command line parameters are available on atlantic driver:
|
||||
|
@ -426,7 +466,7 @@ Support
|
|||
|
||||
If an issue is identified with the released source code on the supported
|
||||
kernel with a supported adapter, email the specific information related
|
||||
to the issue to support@aquantia.com
|
||||
to the issue to aqn_support@marvell.com
|
||||
|
||||
License
|
||||
=======
|
||||
|
|
|
@ -129,9 +129,9 @@ CONFIG_AQUANTIA_PHY=y
|
|||
DPAA Ethernet Frame Processing
|
||||
==============================
|
||||
|
||||
On Rx, buffers for the incoming frames are retrieved from one of the three
|
||||
existing buffers pools. The driver initializes and seeds these, each with
|
||||
buffers of different sizes: 1KB, 2KB and 4KB.
|
||||
On Rx, buffers for the incoming frames are retrieved from the buffers found
|
||||
in the dedicated interface buffer pool. The driver initializes and seeds these
|
||||
with one page buffers.
|
||||
|
||||
On Tx, all transmitted frames are returned to the driver through Tx
|
||||
confirmation frame queues. The driver is then responsible for freeing the
|
||||
|
@ -254,7 +254,7 @@ The following statistics are exported for each interface through ethtool:
|
|||
The driver also exports the following information in sysfs:
|
||||
|
||||
- the FQ IDs for each FQ type
|
||||
/sys/devices/platform/dpaa-ethernet.0/net/<int>/fqids
|
||||
/sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/fqids
|
||||
|
||||
- the IDs of the buffer pools in use
|
||||
/sys/devices/platform/dpaa-ethernet.0/net/<int>/bpids
|
||||
- the ID of the buffer pool in use
|
||||
/sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/bpids
|
||||
|
|
|
@ -8,3 +8,4 @@ DPAA2 Documentation
|
|||
overview
|
||||
dpio-driver
|
||||
ethernet-driver
|
||||
mac-phy-support
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
=======================
|
||||
DPAA2 MAC / PHY support
|
||||
=======================
|
||||
|
||||
:Copyright: |copy| 2019 NXP
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The DPAA2 MAC / PHY support consists of a set of APIs that help DPAA2 network
|
||||
drivers (dpaa2-eth, dpaa2-ethsw) interract with the PHY library.
|
||||
|
||||
DPAA2 Software Architecture
|
||||
---------------------------
|
||||
|
||||
Among other DPAA2 objects, the fsl-mc bus exports DPNI objects (abstracting a
|
||||
network interface) and DPMAC objects (abstracting a MAC). The dpaa2-eth driver
|
||||
probes on the DPNI object and connects to and configures a DPMAC object with
|
||||
the help of phylink.
|
||||
|
||||
Data connections may be established between a DPNI and a DPMAC, or between two
|
||||
DPNIs. Depending on the connection type, the netif_carrier_[on/off] is handled
|
||||
directly by the dpaa2-eth driver or by phylink.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Sources of abstracted link state information presented by the MC firmware
|
||||
|
||||
+--------------------------------------+
|
||||
+------------+ +---------+ | xgmac_mdio |
|
||||
| net_device | | phylink |--| +-----+ +-----+ +-----+ +-----+ |
|
||||
+------------+ +---------+ | | PHY | | PHY | | PHY | | PHY | |
|
||||
| | | +-----+ +-----+ +-----+ +-----+ |
|
||||
+------------------------------------+ | External MDIO bus |
|
||||
| dpaa2-eth | +--------------------------------------+
|
||||
+------------------------------------+
|
||||
| | Linux
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
| | MC firmware
|
||||
| /| V
|
||||
+----------+ / | +----------+
|
||||
| | / | | |
|
||||
| | | | | |
|
||||
| DPNI |<------| |<------| DPMAC |
|
||||
| | | | | |
|
||||
| | \ |<---+ | |
|
||||
+----------+ \ | | +----------+
|
||||
\| |
|
||||
|
|
||||
+--------------------------------------+
|
||||
| MC firmware polling MAC PCS for link |
|
||||
| +-----+ +-----+ +-----+ +-----+ |
|
||||
| | PCS | | PCS | | PCS | | PCS | |
|
||||
| +-----+ +-----+ +-----+ +-----+ |
|
||||
| Internal MDIO bus |
|
||||
+--------------------------------------+
|
||||
|
||||
|
||||
Depending on an MC firmware configuration setting, each MAC may be in one of two modes:
|
||||
|
||||
- DPMAC_LINK_TYPE_FIXED: the link state management is handled exclusively by
|
||||
the MC firmware by polling the MAC PCS. Without the need to register a
|
||||
phylink instance, the dpaa2-eth driver will not bind to the connected dpmac
|
||||
object at all.
|
||||
|
||||
- DPMAC_LINK_TYPE_PHY: The MC firmware is left waiting for link state update
|
||||
events, but those are in fact passed strictly between the dpaa2-mac (based on
|
||||
phylink) and its attached net_device driver (dpaa2-eth, dpaa2-ethsw),
|
||||
effectively bypassing the firmware.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
At probe time or when a DPNI's endpoint is dynamically changed, the dpaa2-eth
|
||||
is responsible to find out if the peer object is a DPMAC and if this is the
|
||||
case, to integrate it with PHYLINK using the dpaa2_mac_connect() API, which
|
||||
will do the following:
|
||||
|
||||
- look up the device tree for PHYLINK-compatible of binding (phy-handle)
|
||||
- will create a PHYLINK instance associated with the received net_device
|
||||
- connect to the PHY using phylink_of_phy_connect()
|
||||
|
||||
The following phylink_mac_ops callback are implemented:
|
||||
|
||||
- .validate() will populate the supported linkmodes with the MAC capabilities
|
||||
only when the phy_interface_t is RGMII_* (at the moment, this is the only
|
||||
link type supported by the driver).
|
||||
|
||||
- .mac_config() will configure the MAC in the new configuration using the
|
||||
dpmac_set_link_state() MC firmware API.
|
||||
|
||||
- .mac_link_up() / .mac_link_down() will update the MAC link using the same
|
||||
API described above.
|
||||
|
||||
At driver unbind() or when the DPNI object is disconnected from the DPMAC, the
|
||||
dpaa2-eth driver calls dpaa2_mac_disconnect() which will, in turn, disconnect
|
||||
from the PHY and destroy the PHYLINK instance.
|
||||
|
||||
In case of a DPNI-DPMAC connection, an 'ip link set dev eth0 up' would start
|
||||
the following sequence of operations:
|
||||
|
||||
(1) phylink_start() called from .dev_open().
|
||||
(2) The .mac_config() and .mac_link_up() callbacks are called by PHYLINK.
|
||||
(3) In order to configure the HW MAC, the MC Firmware API
|
||||
dpmac_set_link_state() is called.
|
||||
(4) The firmware will eventually setup the HW MAC in the new configuration.
|
||||
(5) A netif_carrier_on() call is made directly from PHYLINK on the associated
|
||||
net_device.
|
||||
(6) The dpaa2-eth driver handles the LINK_STATE_CHANGE irq in order to
|
||||
enable/disable Rx taildrop based on the pause frame settings.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
+---------+ +---------+
|
||||
| PHYLINK |-------------->| eth0 |
|
||||
+---------+ (5) +---------+
|
||||
(1) ^ |
|
||||
| |
|
||||
| v (2)
|
||||
+-----------------------------------+
|
||||
| dpaa2-eth |
|
||||
+-----------------------------------+
|
||||
| ^ (6)
|
||||
| |
|
||||
v (3) |
|
||||
+---------+---------------+---------+
|
||||
| DPMAC | | DPNI |
|
||||
+---------+ +---------+
|
||||
| MC Firmware |
|
||||
+-----------------------------------+
|
||||
|
|
||||
|
|
||||
v (4)
|
||||
+-----------------------------------+
|
||||
| HW MAC |
|
||||
+-----------------------------------+
|
||||
|
||||
In case of a DPNI-DPNI connection, a usual sequence of operations looks like
|
||||
the following:
|
||||
|
||||
(1) ip link set dev eth0 up
|
||||
(2) The dpni_enable() MC API called on the associated fsl_mc_device.
|
||||
(3) ip link set dev eth1 up
|
||||
(4) The dpni_enable() MC API called on the associated fsl_mc_device.
|
||||
(5) The LINK_STATE_CHANGED irq is received by both instances of the dpaa2-eth
|
||||
driver because now the operational link state is up.
|
||||
(6) The netif_carrier_on() is called on the exported net_device from
|
||||
link_state_update().
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
+---------+ +---------+
|
||||
| eth0 | | eth1 |
|
||||
+---------+ +---------+
|
||||
| ^ ^ |
|
||||
| | | |
|
||||
(1) v | (6) (6) | v (3)
|
||||
+---------+ +---------+
|
||||
|dpaa2-eth| |dpaa2-eth|
|
||||
+---------+ +---------+
|
||||
| ^ ^ |
|
||||
| | | |
|
||||
(2) v | (5) (5) | v (4)
|
||||
+---------+---------------+---------+
|
||||
| DPNI | | DPNI |
|
||||
+---------+ +---------+
|
||||
| MC Firmware |
|
||||
+-----------------------------------+
|
||||
|
||||
|
||||
Exported API
|
||||
------------
|
||||
|
||||
Any DPAA2 driver that drivers endpoints of DPMAC objects should service its
|
||||
_EVENT_ENDPOINT_CHANGED irq and connect/disconnect from the associated DPMAC
|
||||
when necessary using the below listed API::
|
||||
|
||||
- int dpaa2_mac_connect(struct dpaa2_mac *mac);
|
||||
- void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
|
||||
|
||||
A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED.
|
||||
One can check for this condition using the below API::
|
||||
|
||||
- bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io);
|
||||
|
||||
Before connection to a MAC, the caller must allocate and populate the
|
||||
dpaa2_mac structure with the associated net_device, a pointer to the MC portal
|
||||
to be used and the actual fsl_mc_device structure of the DPMAC.
|
|
@ -154,6 +154,27 @@ User command examples:
|
|||
values:
|
||||
cmode runtime value smfs
|
||||
|
||||
enable_roce: RoCE enablement state
|
||||
----------------------------------
|
||||
RoCE enablement state controls driver support for RoCE traffic.
|
||||
When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well known UDP RoCE port is handled as raw ethernet traffic.
|
||||
|
||||
To change RoCE enablement state a user must change the driverinit cmode value and run devlink reload.
|
||||
|
||||
User command examples:
|
||||
|
||||
- Disable RoCE::
|
||||
|
||||
$ devlink dev param set pci/0000:06:00.0 name enable_roce value false cmode driverinit
|
||||
$ devlink dev reload pci/0000:06:00.0
|
||||
|
||||
- Read RoCE enablement state::
|
||||
|
||||
$ devlink dev param show pci/0000:06:00.0 name enable_roce
|
||||
pci/0000:06:00.0:
|
||||
name enable_roce type generic
|
||||
values:
|
||||
cmode driverinit value true
|
||||
|
||||
Devlink health reporters
|
||||
========================
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
* Texas Instruments CPSW switchdev based ethernet driver 2.0
|
||||
|
||||
- Port renaming
|
||||
On older udev versions renaming of ethX to swXpY will not be automatically
|
||||
supported
|
||||
In order to rename via udev:
|
||||
ip -d link show dev sw0p1 | grep switchid
|
||||
|
||||
SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
|
||||
ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
|
||||
|
||||
|
||||
====================
|
||||
# Dual mac mode
|
||||
====================
|
||||
- The new (cpsw_new.c) driver is operating in dual-emac mode by default, thus
|
||||
working as 2 individual network interfaces. Main differences from legacy CPSW
|
||||
driver are:
|
||||
- optimized promiscuous mode: The P0_UNI_FLOOD (both ports) is enabled in
|
||||
addition to ALLMULTI (current port) instead of ALE_BYPASS.
|
||||
So, Ports in promiscuous mode will keep possibility of mcast and vlan filtering,
|
||||
which is provides significant benefits when ports are joined to the same bridge,
|
||||
but without enabling "switch" mode, or to different bridges.
|
||||
- learning disabled on ports as it make not too much sense for
|
||||
segregated ports - no forwarding in HW.
|
||||
- enabled basic support for devlink.
|
||||
|
||||
devlink dev show
|
||||
platform/48484000.switch
|
||||
|
||||
devlink dev param show
|
||||
platform/48484000.switch:
|
||||
name switch_mode type driver-specific
|
||||
values:
|
||||
cmode runtime value false
|
||||
name ale_bypass type driver-specific
|
||||
values:
|
||||
cmode runtime value false
|
||||
|
||||
Devlink configuration parameters
|
||||
====================
|
||||
See Documentation/networking/devlink-params-ti-cpsw-switch.txt
|
||||
|
||||
====================
|
||||
# Bridging in dual mac mode
|
||||
====================
|
||||
The dual_mac mode requires two vids to be reserved for internal purposes,
|
||||
which, by default, equal CPSW Port numbers. As result, bridge has to be
|
||||
configured in vlan unaware mode or default_pvid has to be adjusted.
|
||||
|
||||
ip link add name br0 type bridge
|
||||
ip link set dev br0 type bridge vlan_filtering 0
|
||||
echo 0 > /sys/class/net/br0/bridge/default_pvid
|
||||
ip link set dev sw0p1 master br0
|
||||
ip link set dev sw0p2 master br0
|
||||
- or -
|
||||
ip link add name br0 type bridge
|
||||
ip link set dev br0 type bridge vlan_filtering 0
|
||||
echo 100 > /sys/class/net/br0/bridge/default_pvid
|
||||
ip link set dev br0 type bridge vlan_filtering 1
|
||||
ip link set dev sw0p1 master br0
|
||||
ip link set dev sw0p2 master br0
|
||||
|
||||
====================
|
||||
# Enabling "switch"
|
||||
====================
|
||||
The Switch mode can be enabled by configuring devlink driver parameter
|
||||
"switch_mode" to 1/true:
|
||||
devlink dev param set platform/48484000.switch \
|
||||
name switch_mode value 1 cmode runtime
|
||||
|
||||
This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
|
||||
Port's netdev devices have to be in UP before joining to the bridge to avoid
|
||||
overwriting of bridge configuration as CPSW switch driver copletly reloads its
|
||||
configuration when first Port changes its state to UP.
|
||||
|
||||
When the both interfaces joined the bridge - CPSW switch driver will enable
|
||||
marking packets with offload_fwd_mark flag unless "ale_bypass=0"
|
||||
|
||||
All configuration is implemented via switchdev API.
|
||||
|
||||
====================
|
||||
# Bridge setup
|
||||
====================
|
||||
devlink dev param set platform/48484000.switch \
|
||||
name switch_mode value 1 cmode runtime
|
||||
|
||||
ip link add name br0 type bridge
|
||||
ip link set dev br0 type bridge ageing_time 1000
|
||||
ip link set dev sw0p1 up
|
||||
ip link set dev sw0p2 up
|
||||
ip link set dev sw0p1 master br0
|
||||
ip link set dev sw0p2 master br0
|
||||
[*] bridge vlan add dev br0 vid 1 pvid untagged self
|
||||
|
||||
[*] if vlan_filtering=1. where default_pvid=1
|
||||
|
||||
=================
|
||||
# On/off STP
|
||||
=================
|
||||
ip link set dev BRDEV type bridge stp_state 1/0
|
||||
|
||||
Note. Steps [*] are mandatory.
|
||||
|
||||
====================
|
||||
# VLAN configuration
|
||||
====================
|
||||
bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
|
||||
|
||||
Note. This step is mandatory for bridge/default_pvid.
|
||||
|
||||
=================
|
||||
# Add extra VLANs
|
||||
=================
|
||||
1. untagged:
|
||||
bridge vlan add dev sw0p1 vid 100 pvid untagged master
|
||||
bridge vlan add dev sw0p2 vid 100 pvid untagged master
|
||||
bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
|
||||
|
||||
2. tagged:
|
||||
bridge vlan add dev sw0p1 vid 100 master
|
||||
bridge vlan add dev sw0p2 vid 100 master
|
||||
bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
|
||||
|
||||
====
|
||||
FDBs
|
||||
====
|
||||
FDBs are automatically added on the appropriate switch port upon detection
|
||||
|
||||
Manually adding FDBs:
|
||||
bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
|
||||
bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
|
||||
|
||||
====
|
||||
MDBs
|
||||
====
|
||||
MDBs are automatically added on the appropriate switch port upon detection
|
||||
|
||||
Manually adding MDBs:
|
||||
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
|
||||
bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
|
||||
|
||||
==================
|
||||
Multicast flooding
|
||||
==================
|
||||
CPU port mcast_flooding is always on
|
||||
|
||||
Turning flooding on/off on swithch ports:
|
||||
bridge link set dev sw0p1 mcast_flood on/off
|
||||
|
||||
==================
|
||||
Access and Trunk port
|
||||
==================
|
||||
bridge vlan add dev sw0p1 vid 100 pvid untagged master
|
||||
bridge vlan add dev sw0p2 vid 100 master
|
||||
|
||||
|
||||
bridge vlan add dev br0 vid 100 self
|
||||
ip link add link br0 name br0.100 type vlan id 100
|
||||
|
||||
Note. Setting PVID on Bridge device itself working only for
|
||||
default VLAN (default_pvid).
|
||||
|
||||
=====================
|
||||
NFS
|
||||
=====================
|
||||
The only way for NFS to work is by chrooting to a minimal environment when
|
||||
switch configuration that will affect connectivity is needed.
|
||||
Assuming you are booting NFS with eth1 interface(the script is hacky and
|
||||
it's just there to prove NFS is doable).
|
||||
|
||||
setup.sh:
|
||||
#!/bin/sh
|
||||
mkdir proc
|
||||
mount -t proc none /proc
|
||||
ifconfig br0 > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Setting up bridge"
|
||||
ip link add name br0 type bridge
|
||||
ip link set dev br0 type bridge ageing_time 1000
|
||||
ip link set dev br0 type bridge vlan_filtering 1
|
||||
|
||||
ip link set eth1 down
|
||||
ip link set eth1 name sw0p1
|
||||
ip link set dev sw0p1 up
|
||||
ip link set dev sw0p2 up
|
||||
ip link set dev sw0p2 master br0
|
||||
ip link set dev sw0p1 master br0
|
||||
bridge vlan add dev br0 vid 1 pvid untagged self
|
||||
ifconfig sw0p1 0.0.0.0
|
||||
udhchc -i br0
|
||||
fi
|
||||
umount /proc
|
||||
|
||||
run_nfs.sh:
|
||||
#!/bin/sh
|
||||
mkdir /tmp/root/bin -p
|
||||
mkdir /tmp/root/lib -p
|
||||
|
||||
cp -r /lib/ /tmp/root/
|
||||
cp -r /bin/ /tmp/root/
|
||||
cp /sbin/ip /tmp/root/bin
|
||||
cp /sbin/bridge /tmp/root/bin
|
||||
cp /sbin/ifconfig /tmp/root/bin
|
||||
cp /sbin/udhcpc /tmp/root/bin
|
||||
cp /path/to/setup.sh /tmp/root/bin
|
||||
chroot /tmp/root/ busybox sh /bin/setup.sh
|
||||
|
||||
run ./run_nfs.sh
|
|
@ -0,0 +1,17 @@
|
|||
flow_steering_mode [DEVICE, DRIVER-SPECIFIC]
|
||||
Controls the flow steering mode of the driver.
|
||||
Two modes are supported:
|
||||
1. 'dmfs' - Device managed flow steering.
|
||||
2. 'smfs - Software/Driver managed flow steering.
|
||||
In DMFS mode, the HW steering entities are created and
|
||||
managed through the Firmware.
|
||||
In SMFS mode, the HW steering entities are created and
|
||||
managed though by the driver directly into Hardware
|
||||
without firmware intervention.
|
||||
Type: String
|
||||
Configuration mode: runtime
|
||||
|
||||
enable_roce [DEVICE, GENERIC]
|
||||
Enable handling of RoCE traffic in the device.
|
||||
Defaultly enabled.
|
||||
Configuration mode: driverinit
|
|
@ -0,0 +1,7 @@
|
|||
ATU_hash [DEVICE, DRIVER-SPECIFIC]
|
||||
Select one of four possible hashing algorithms for
|
||||
MAC addresses in the Address Translation Unit.
|
||||
A value of 3 seems to work better than the default of
|
||||
1 when many MAC addresses have the same OUI.
|
||||
Configuration mode: runtime
|
||||
Type: u8. 0-3 valid.
|
|
@ -0,0 +1,10 @@
|
|||
ale_bypass [DEVICE, DRIVER-SPECIFIC]
|
||||
Allows to enable ALE_CONTROL(4).BYPASS mode for debug purposes.
|
||||
All packets will be sent to the Host port only if enabled.
|
||||
Type: bool
|
||||
Configuration mode: runtime
|
||||
|
||||
switch_mode [DEVICE, DRIVER-SPECIFIC]
|
||||
Enable switch mode
|
||||
Type: bool
|
||||
Configuration mode: runtime
|
|
@ -65,3 +65,7 @@ reset_dev_on_drv_probe [DEVICE, GENERIC]
|
|||
Reset only if device firmware can be found in the
|
||||
filesystem.
|
||||
Type: u8
|
||||
|
||||
enable_roce [DEVICE, GENERIC]
|
||||
Enable handling of RoCE traffic in the device.
|
||||
Type: Boolean
|
||||
|
|
|
@ -162,6 +162,67 @@ be added to the following table:
|
|||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they could not be
|
||||
enqueued to a transmission queue which is full
|
||||
* - ``non_ip``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to
|
||||
undergo a layer 3 lookup, but are not IP or MPLS packets
|
||||
* - ``uc_dip_over_mc_dmac``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and they have a unicast destination IP and a multicast destination
|
||||
MAC
|
||||
* - ``dip_is_loopback_address``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and their destination IP is the loopback address (i.e., 127.0.0.0/8
|
||||
and ::1/128)
|
||||
* - ``sip_is_mc``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and their source IP is multicast (i.e., 224.0.0.0/8 and ff::/8)
|
||||
* - ``sip_is_loopback_address``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and their source IP is the loopback address (i.e., 127.0.0.0/8 and ::1/128)
|
||||
* - ``ip_header_corrupted``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and their IP header is corrupted: wrong checksum, wrong IP version
|
||||
or too short Internet Header Length (IHL)
|
||||
* - ``ipv4_sip_is_limited_bc``
|
||||
- ``drop``
|
||||
- Traps packets that the device decided to drop because they need to be
|
||||
routed and their source IP is limited broadcast (i.e., 255.255.255.255/32)
|
||||
* - ``ipv6_mc_dip_reserved_scope``
|
||||
- ``drop``
|
||||
- Traps IPv6 packets that the device decided to drop because they need to
|
||||
be routed and their IPv6 multicast destination IP has a reserved scope
|
||||
(i.e., ffx0::/16)
|
||||
* - ``ipv6_mc_dip_interface_local_scope``
|
||||
- ``drop``
|
||||
- Traps IPv6 packets that the device decided to drop because they need to
|
||||
be routed and their IPv6 multicast destination IP has an interface-local scope
|
||||
(i.e., ffx1::/16)
|
||||
* - ``mtu_value_is_too_small``
|
||||
- ``exception``
|
||||
- Traps packets that should have been routed by the device, but were bigger
|
||||
than the MTU of the egress interface
|
||||
* - ``unresolved_neigh``
|
||||
- ``exception``
|
||||
- Traps packets that did not have a matching IP neighbour after routing
|
||||
* - ``mc_reverse_path_forwarding``
|
||||
- ``exception``
|
||||
- Traps multicast IP packets that failed reverse-path forwarding (RPF)
|
||||
check during multicast routing
|
||||
* - ``reject_route``
|
||||
- ``exception``
|
||||
- Traps packets that hit reject routes (i.e., "unreachable", "prohibit")
|
||||
* - ``ipv4_lpm_miss``
|
||||
- ``exception``
|
||||
- Traps unicast IPv4 packets that did not match any route
|
||||
* - ``ipv6_lpm_miss``
|
||||
- ``exception``
|
||||
- Traps unicast IPv6 packets that did not match any route
|
||||
|
||||
Driver-specific Packet Traps
|
||||
============================
|
||||
|
|
|
@ -770,10 +770,10 @@ Some core changes of the new internal format:
|
|||
callq foo
|
||||
mov %rax,%r13
|
||||
mov %rbx,%rdi
|
||||
mov $0x2,%esi
|
||||
mov $0x3,%edx
|
||||
mov $0x4,%ecx
|
||||
mov $0x5,%r8d
|
||||
mov $0x6,%esi
|
||||
mov $0x7,%edx
|
||||
mov $0x8,%ecx
|
||||
mov $0x9,%r8d
|
||||
callq bar
|
||||
add %r13,%rax
|
||||
mov -0x228(%rbp),%rbx
|
||||
|
|
|
@ -33,6 +33,7 @@ Contents:
|
|||
scaling
|
||||
tls
|
||||
tls-offload
|
||||
nfc
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
|
|
@ -2091,6 +2091,28 @@ pf_enable - INTEGER
|
|||
|
||||
Default: 1
|
||||
|
||||
pf_expose - INTEGER
|
||||
Unset or enable/disable pf (pf is short for potentially failed) state
|
||||
exposure. Applications can control the exposure of the PF path state
|
||||
in the SCTP_PEER_ADDR_CHANGE event and the SCTP_GET_PEER_ADDR_INFO
|
||||
sockopt. When it's unset, no SCTP_PEER_ADDR_CHANGE event with
|
||||
SCTP_ADDR_PF state will be sent and a SCTP_PF-state transport info
|
||||
can be got via SCTP_GET_PEER_ADDR_INFO sockopt; When it's enabled,
|
||||
a SCTP_PEER_ADDR_CHANGE event will be sent for a transport becoming
|
||||
SCTP_PF state and a SCTP_PF-state transport info can be got via
|
||||
SCTP_GET_PEER_ADDR_INFO sockopt; When it's diabled, no
|
||||
SCTP_PEER_ADDR_CHANGE event will be sent and it returns -EACCES when
|
||||
trying to get a SCTP_PF-state transport info via SCTP_GET_PEER_ADDR_INFO
|
||||
sockopt.
|
||||
|
||||
0: Unset pf state exposure, Compatible with old applications.
|
||||
|
||||
1: Disable pf state exposure.
|
||||
|
||||
2: Enable pf state exposure.
|
||||
|
||||
Default: 0
|
||||
|
||||
addip_noauth_enable - BOOLEAN
|
||||
Dynamic Address Reconfiguration (ADD-IP) requires the use of
|
||||
authentication to protect the operations of adding or removing new
|
||||
|
@ -2173,6 +2195,18 @@ pf_retrans - INTEGER
|
|||
|
||||
Default: 0
|
||||
|
||||
ps_retrans - INTEGER
|
||||
Primary.Switchover.Max.Retrans (PSMR), it's a tunable parameter coming
|
||||
from section-5 "Primary Path Switchover" in rfc7829. The primary path
|
||||
will be changed to another active path when the path error counter on
|
||||
the old primary path exceeds PSMR, so that "the SCTP sender is allowed
|
||||
to continue data transmission on a new working path even when the old
|
||||
primary destination address becomes active again". Note this feature
|
||||
is disabled by initializing 'ps_retrans' per netns as 0xffff by default,
|
||||
and its value can't be less than 'pf_retrans' when changing by sysctl.
|
||||
|
||||
Default: 0xffff
|
||||
|
||||
rto_initial - INTEGER
|
||||
The initial round trip timeout value in milliseconds that will be used
|
||||
in calculating round trip times. This is the initial time interval
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
===================
|
||||
Linux NFC subsystem
|
||||
===================
|
||||
|
||||
|
@ -8,7 +9,7 @@ This document covers the architecture overview, the device driver interface
|
|||
description and the userspace interface description.
|
||||
|
||||
Architecture overview
|
||||
---------------------
|
||||
=====================
|
||||
|
||||
The NFC subsystem is responsible for:
|
||||
- NFC adapters management;
|
||||
|
@ -25,33 +26,34 @@ The control operations are available to userspace via generic netlink.
|
|||
The low-level data exchange interface is provided by the new socket family
|
||||
PF_NFC. The NFC_SOCKPROTO_RAW performs raw communication with NFC targets.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
+--------------------------------------+
|
||||
| USER SPACE |
|
||||
+--------------------------------------+
|
||||
^ ^
|
||||
| low-level | control
|
||||
| data exchange | operations
|
||||
| |
|
||||
| v
|
||||
| +-----------+
|
||||
| AF_NFC | netlink |
|
||||
| socket +-----------+
|
||||
| raw ^
|
||||
| |
|
||||
v v
|
||||
+---------+ +-----------+
|
||||
| rawsock | <--------> | core |
|
||||
+---------+ +-----------+
|
||||
^
|
||||
|
|
||||
v
|
||||
+-----------+
|
||||
| driver |
|
||||
+-----------+
|
||||
+--------------------------------------+
|
||||
| USER SPACE |
|
||||
+--------------------------------------+
|
||||
^ ^
|
||||
| low-level | control
|
||||
| data exchange | operations
|
||||
| |
|
||||
| v
|
||||
| +-----------+
|
||||
| AF_NFC | netlink |
|
||||
| socket +-----------+
|
||||
| raw ^
|
||||
| |
|
||||
v v
|
||||
+---------+ +-----------+
|
||||
| rawsock | <--------> | core |
|
||||
+---------+ +-----------+
|
||||
^
|
||||
|
|
||||
v
|
||||
+-----------+
|
||||
| driver |
|
||||
+-----------+
|
||||
|
||||
Device Driver Interface
|
||||
-----------------------
|
||||
=======================
|
||||
|
||||
When registering on the NFC subsystem, the device driver must inform the core
|
||||
of the set of supported NFC protocols and the set of ops callbacks. The ops
|
||||
|
@ -64,7 +66,7 @@ callbacks that must be implemented are the following:
|
|||
* data_exchange - send data and receive the response (transceive operation)
|
||||
|
||||
Userspace interface
|
||||
--------------------
|
||||
===================
|
||||
|
||||
The userspace interface is divided in control operations and low-level data
|
||||
exchange operation.
|
||||
|
@ -82,7 +84,7 @@ The operations are composed by commands and events, all listed below:
|
|||
* NFC_EVENT_DEVICE_ADDED - reports an NFC device addition
|
||||
* NFC_EVENT_DEVICE_REMOVED - reports an NFC device removal
|
||||
* NFC_EVENT_TARGETS_FOUND - reports START_POLL results when 1 or more targets
|
||||
are found
|
||||
are found
|
||||
|
||||
The user must call START_POLL to poll for NFC targets, passing the desired NFC
|
||||
protocols through NFC_ATTR_PROTOCOLS attribute. The device remains in polling
|
||||
|
@ -101,14 +103,14 @@ it's closed.
|
|||
LOW-LEVEL DATA EXCHANGE:
|
||||
|
||||
The userspace must use PF_NFC sockets to perform any data communication with
|
||||
targets. All NFC sockets use AF_NFC:
|
||||
targets. All NFC sockets use AF_NFC::
|
||||
|
||||
struct sockaddr_nfc {
|
||||
sa_family_t sa_family;
|
||||
__u32 dev_idx;
|
||||
__u32 target_idx;
|
||||
__u32 nfc_protocol;
|
||||
};
|
||||
struct sockaddr_nfc {
|
||||
sa_family_t sa_family;
|
||||
__u32 dev_idx;
|
||||
__u32 target_idx;
|
||||
__u32 nfc_protocol;
|
||||
};
|
||||
|
||||
To establish a connection with one target, the user must create an
|
||||
NFC_SOCKPROTO_RAW socket and call the 'connect' syscall with the sockaddr_nfc
|
|
@ -352,7 +352,8 @@ Fills the phydev structure with up-to-date information about the current
|
|||
settings in the PHY.
|
||||
::
|
||||
|
||||
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
|
||||
int phy_ethtool_ksettings_set(struct phy_device *phydev,
|
||||
const struct ethtool_link_ksettings *cmd);
|
||||
|
||||
Ethtool convenience functions.
|
||||
::
|
||||
|
|
|
@ -213,3 +213,29 @@ A patchset to OpenSSL to use ktls as the record layer is
|
|||
of calling send directly after a handshake using gnutls.
|
||||
Since it doesn't implement a full record layer, control
|
||||
messages are not supported.
|
||||
|
||||
Statistics
|
||||
==========
|
||||
|
||||
TLS implementation exposes the following per-namespace statistics
|
||||
(``/proc/net/tls_stat``):
|
||||
|
||||
- ``TlsCurrTxSw``, ``TlsCurrRxSw`` -
|
||||
number of TX and RX sessions currently installed where host handles
|
||||
cryptography
|
||||
|
||||
- ``TlsCurrTxDevice``, ``TlsCurrRxDevice`` -
|
||||
number of TX and RX sessions currently installed where NIC handles
|
||||
cryptography
|
||||
|
||||
- ``TlsTxSw``, ``TlsRxSw`` -
|
||||
number of TX and RX sessions opened with host cryptography
|
||||
|
||||
- ``TlsTxDevice``, ``TlsRxDevice`` -
|
||||
number of TX and RX sessions opened with NIC cryptography
|
||||
|
||||
- ``TlsDecryptError`` -
|
||||
record decryption failed (e.g. due to incorrect authentication tag)
|
||||
|
||||
- ``TlsDeviceRxResync`` -
|
||||
number of RX resyncs sent to NICs handling cryptography
|
||||
|
|
33
MAINTAINERS
33
MAINTAINERS
|
@ -1182,14 +1182,21 @@ S: Maintained
|
|||
F: drivers/media/i2c/aptina-pll.*
|
||||
|
||||
AQUANTIA ETHERNET DRIVER (atlantic)
|
||||
M: Igor Russkikh <igor.russkikh@aquantia.com>
|
||||
M: Igor Russkikh <irusskikh@marvell.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://www.aquantia.com
|
||||
W: https://www.marvell.com/
|
||||
Q: http://patchwork.ozlabs.org/project/netdev/list/
|
||||
F: drivers/net/ethernet/aquantia/atlantic/
|
||||
F: Documentation/networking/device_drivers/aquantia/atlantic.txt
|
||||
|
||||
AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM
|
||||
M: Egor Pomozov <epomozov@marvell.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://www.aquantia.com
|
||||
F: drivers/net/ethernet/aquantia/atlantic/aq_ptp*
|
||||
|
||||
ARC FRAMEBUFFER DRIVER
|
||||
M: Jaya Kumar <jayalk@intworks.biz>
|
||||
S: Maintained
|
||||
|
@ -5062,10 +5069,14 @@ M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
|
|||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-eth*
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpni*
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpmac*
|
||||
F: drivers/net/ethernet/freescale/dpaa2/dpkg.h
|
||||
F: drivers/net/ethernet/freescale/dpaa2/Makefile
|
||||
F: drivers/net/ethernet/freescale/dpaa2/Kconfig
|
||||
F: Documentation/networking/device_drivers/freescale/dpaa2/ethernet-driver.rst
|
||||
F: Documentation/networking/device_drivers/freescale/dpaa2/mac-phy-support.rst
|
||||
|
||||
DPAA2 ETHERNET SWITCH DRIVER
|
||||
M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
|
||||
|
@ -6159,10 +6170,12 @@ S: Maintained
|
|||
F: Documentation/ABI/testing/sysfs-class-net-phydev
|
||||
F: Documentation/devicetree/bindings/net/ethernet-phy.yaml
|
||||
F: Documentation/devicetree/bindings/net/mdio*
|
||||
F: Documentation/devicetree/bindings/net/qca,ar803x.yaml
|
||||
F: Documentation/networking/phy.rst
|
||||
F: drivers/net/phy/
|
||||
F: drivers/of/of_mdio.c
|
||||
F: drivers/of/of_net.c
|
||||
F: include/dt-bindings/net/qca-ar803x.h
|
||||
F: include/linux/*mdio*.h
|
||||
F: include/linux/of_net.h
|
||||
F: include/linux/phy.h
|
||||
|
@ -7482,8 +7495,8 @@ F: drivers/platform/x86/tc1100-wmi.c
|
|||
|
||||
HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
|
||||
M: Jaroslav Kysela <perex@perex.cz>
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/hp/hp100.*
|
||||
S: Obsolete
|
||||
F: drivers/staging/hp/hp100.*
|
||||
|
||||
HPET: High Precision Event Timers driver
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
|
@ -9798,6 +9811,7 @@ S: Maintained
|
|||
F: drivers/net/dsa/mv88e6xxx/
|
||||
F: include/linux/platform_data/mv88e6xxx.h
|
||||
F: Documentation/devicetree/bindings/net/dsa/marvell.txt
|
||||
F: Documentation/networking/devlink-params-mv88e6xxx.txt
|
||||
|
||||
MARVELL ARMADA DRM SUPPORT
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
|
@ -10877,6 +10891,7 @@ M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
|
|||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/ethernet/mscc/
|
||||
F: include/soc/mscc/ocelot*
|
||||
|
||||
MICROSOFT SURFACE PRO 3 BUTTON DRIVER
|
||||
M: Chen Yu <yu.c.chen@intel.com>
|
||||
|
@ -13881,7 +13896,7 @@ R: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
|
|||
L: netdev@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
F: Documentation/devicetree/bindings/net/renesas,*.txt
|
||||
F: Documentation/devicetree/bindings/net/sh_eth.txt
|
||||
F: Documentation/devicetree/bindings/net/renesas,*.yaml
|
||||
F: drivers/net/ethernet/renesas/
|
||||
F: include/linux/sh_eth.h
|
||||
|
||||
|
@ -17405,6 +17420,14 @@ S: Maintained
|
|||
F: drivers/input/serio/userio.c
|
||||
F: include/uapi/linux/userio.h
|
||||
|
||||
VITESSE FELIX ETHERNET SWITCH DRIVER
|
||||
M: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
M: Claudiu Manoil <claudiu.manoil@nxp.com>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/dsa/ocelot/*
|
||||
F: net/dsa/tag_ocelot.c
|
||||
|
||||
VIVID VIRTUAL VIDEO DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -186,3 +186,30 @@
|
|||
pinctrl-1 = <&mmc2_pins_hs>;
|
||||
pinctrl-2 = <&mmc2_pins_ddr_rev20 &mmc2_iodelay_ddr_conf>;
|
||||
};
|
||||
|
||||
&mac_sw {
|
||||
pinctrl-names = "default", "sleep";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&cpsw_port1 {
|
||||
phy-handle = <ðphy0_sw>;
|
||||
phy-mode = "rgmii";
|
||||
ti,dual-emac-pvid = <1>;
|
||||
};
|
||||
|
||||
&cpsw_port2 {
|
||||
phy-handle = <ðphy1_sw>;
|
||||
phy-mode = "rgmii";
|
||||
ti,dual-emac-pvid = <2>;
|
||||
};
|
||||
|
||||
&davinci_mdio_sw {
|
||||
ethphy0_sw: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
ethphy1_sw: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,3 +27,8 @@
|
|||
pinctrl-1 = <&mmc2_pins_hs>;
|
||||
pinctrl-2 = <&mmc2_pins_ddr_rev20>;
|
||||
};
|
||||
|
||||
&mac {
|
||||
status = "okay";
|
||||
dual_emac;
|
||||
};
|
||||
|
|
|
@ -35,3 +35,8 @@
|
|||
pinctrl-1 = <&mmc2_pins_default>;
|
||||
pinctrl-2 = <&mmc2_pins_default>;
|
||||
};
|
||||
|
||||
&mac {
|
||||
status = "okay";
|
||||
dual_emac;
|
||||
};
|
||||
|
|
|
@ -363,11 +363,6 @@
|
|||
ext-clk-src;
|
||||
};
|
||||
|
||||
&mac {
|
||||
status = "okay";
|
||||
dual_emac;
|
||||
};
|
||||
|
||||
&cpsw_emac0 {
|
||||
phy-handle = <ðphy0>;
|
||||
phy-mode = "rgmii";
|
||||
|
|
|
@ -3079,6 +3079,58 @@
|
|||
phys = <&phy_gmii_sel 2>;
|
||||
};
|
||||
};
|
||||
|
||||
mac_sw: switch@0 {
|
||||
compatible = "ti,dra7-cpsw-switch","ti,cpsw-switch";
|
||||
reg = <0x0 0x4000>;
|
||||
ranges = <0 0 0x4000>;
|
||||
clocks = <&gmac_main_clk>;
|
||||
clock-names = "fck";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
syscon = <&scm_conf>;
|
||||
status = "disabled";
|
||||
|
||||
interrupts = <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "rx_thresh", "rx", "tx", "misc";
|
||||
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpsw_port1: port@1 {
|
||||
reg = <1>;
|
||||
label = "port1";
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1>;
|
||||
};
|
||||
|
||||
cpsw_port2: port@2 {
|
||||
reg = <2>;
|
||||
label = "port2";
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2>;
|
||||
};
|
||||
};
|
||||
|
||||
davinci_mdio_sw: mdio@1000 {
|
||||
compatible = "ti,cpsw-mdio","ti,davinci_mdio";
|
||||
clocks = <&gmac_main_clk>;
|
||||
clock-names = "fck";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
bus_freq = <1000000>;
|
||||
reg = <0x1000 0x100>;
|
||||
};
|
||||
|
||||
cpts {
|
||||
clocks = <&gmac_clkctrl DRA7_GMAC_GMAC_CLKCTRL 25>;
|
||||
clock-names = "cpts";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -554,3 +554,4 @@ CONFIG_DEBUG_INFO_DWARF4=y
|
|||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_SCHEDSTATS=y
|
||||
# CONFIG_DEBUG_BUGVERBOSE is not set
|
||||
CONFIG_TI_CPSW_SWITCHDEV=y
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
@ -22,7 +23,6 @@
|
|||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
#include <linux/can/platform/mcp251x.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
#include "generic.h"
|
||||
|
@ -69,8 +69,9 @@ static struct pxa2xx_spi_chip mcp251x_chip_info4 = {
|
|||
.gpio_cs = ICONTROL_MCP251x_nCS4
|
||||
};
|
||||
|
||||
static struct mcp251x_platform_data mcp251x_info = {
|
||||
.oscillator_frequency = 16E6,
|
||||
static const struct property_entry mcp251x_properties[] = {
|
||||
PROPERTY_ENTRY_U32("clock-frequency", 16000000),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct spi_board_info mcp251x_board_info[] = {
|
||||
|
@ -79,7 +80,7 @@ static struct spi_board_info mcp251x_board_info[] = {
|
|||
.max_speed_hz = 6500000,
|
||||
.bus_num = 3,
|
||||
.chip_select = 0,
|
||||
.platform_data = &mcp251x_info,
|
||||
.properties = mcp251x_properties,
|
||||
.controller_data = &mcp251x_chip_info1,
|
||||
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ1)
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/leds.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/serial_8250.h>
|
||||
|
@ -27,7 +28,6 @@
|
|||
#include <linux/platform_data/i2c-pxa.h>
|
||||
#include <linux/platform_data/pca953x.h>
|
||||
#include <linux/apm-emulation.h>
|
||||
#include <linux/can/platform/mcp251x.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
|
@ -428,14 +428,15 @@ static struct gpiod_lookup_table can_regulator_gpiod_table = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct mcp251x_platform_data zeus_mcp2515_pdata = {
|
||||
.oscillator_frequency = 16*1000*1000,
|
||||
static const struct property_entry mcp251x_properties[] = {
|
||||
PROPERTY_ENTRY_U32("clock-frequency", 16000000),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct spi_board_info zeus_spi_board_info[] = {
|
||||
[0] = {
|
||||
.modalias = "mcp2515",
|
||||
.platform_data = &zeus_mcp2515_pdata,
|
||||
.properties = mcp251x_properties,
|
||||
.irq = PXA_GPIO_TO_IRQ(ZEUS_CAN_GPIO),
|
||||
.max_speed_hz = 1*1000*1000,
|
||||
.bus_num = 3,
|
||||
|
|
|
@ -17,7 +17,7 @@ typedef struct
|
|||
|
||||
#define LOCAL_INIT(i) { (i) }
|
||||
|
||||
static __inline__ long local_read(local_t *l)
|
||||
static __inline__ long local_read(const local_t *l)
|
||||
{
|
||||
return READ_ONCE(l->v);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <linux/filter.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/dis.h>
|
||||
#include <asm/facility.h>
|
||||
|
@ -38,10 +40,11 @@ struct bpf_jit {
|
|||
int size; /* Size of program and literal pool */
|
||||
int size_prg; /* Size of program */
|
||||
int prg; /* Current position in program */
|
||||
int lit_start; /* Start of literal pool */
|
||||
int lit; /* Current position in literal pool */
|
||||
int lit32_start; /* Start of 32-bit literal pool */
|
||||
int lit32; /* Current position in 32-bit literal pool */
|
||||
int lit64_start; /* Start of 64-bit literal pool */
|
||||
int lit64; /* Current position in 64-bit literal pool */
|
||||
int base_ip; /* Base address for literal pool */
|
||||
int ret0_ip; /* Address of return 0 */
|
||||
int exit_ip; /* Address of exit */
|
||||
int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */
|
||||
int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */
|
||||
|
@ -49,14 +52,10 @@ struct bpf_jit {
|
|||
int labels[1]; /* Labels for local jumps */
|
||||
};
|
||||
|
||||
#define BPF_SIZE_MAX 0xffff /* Max size for program (16 bit branches) */
|
||||
|
||||
#define SEEN_MEM (1 << 0) /* use mem[] for temporary storage */
|
||||
#define SEEN_RET0 (1 << 1) /* ret0_ip points to a valid return 0 */
|
||||
#define SEEN_LITERAL (1 << 2) /* code uses literals */
|
||||
#define SEEN_FUNC (1 << 3) /* calls C functions */
|
||||
#define SEEN_TAIL_CALL (1 << 4) /* code uses tail calls */
|
||||
#define SEEN_REG_AX (1 << 5) /* code uses constant blinding */
|
||||
#define SEEN_MEM BIT(0) /* use mem[] for temporary storage */
|
||||
#define SEEN_LITERAL BIT(1) /* code uses literals */
|
||||
#define SEEN_FUNC BIT(2) /* calls C functions */
|
||||
#define SEEN_TAIL_CALL BIT(3) /* code uses tail calls */
|
||||
#define SEEN_STACK (SEEN_FUNC | SEEN_MEM)
|
||||
|
||||
/*
|
||||
|
@ -131,13 +130,13 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define _EMIT2(op) \
|
||||
({ \
|
||||
if (jit->prg_buf) \
|
||||
*(u16 *) (jit->prg_buf + jit->prg) = op; \
|
||||
*(u16 *) (jit->prg_buf + jit->prg) = (op); \
|
||||
jit->prg += 2; \
|
||||
})
|
||||
|
||||
#define EMIT2(op, b1, b2) \
|
||||
({ \
|
||||
_EMIT2(op | reg(b1, b2)); \
|
||||
_EMIT2((op) | reg(b1, b2)); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
@ -145,20 +144,20 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define _EMIT4(op) \
|
||||
({ \
|
||||
if (jit->prg_buf) \
|
||||
*(u32 *) (jit->prg_buf + jit->prg) = op; \
|
||||
*(u32 *) (jit->prg_buf + jit->prg) = (op); \
|
||||
jit->prg += 4; \
|
||||
})
|
||||
|
||||
#define EMIT4(op, b1, b2) \
|
||||
({ \
|
||||
_EMIT4(op | reg(b1, b2)); \
|
||||
_EMIT4((op) | reg(b1, b2)); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
||||
#define EMIT4_RRF(op, b1, b2, b3) \
|
||||
({ \
|
||||
_EMIT4(op | reg_high(b3) << 8 | reg(b1, b2)); \
|
||||
_EMIT4((op) | reg_high(b3) << 8 | reg(b1, b2)); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
REG_SET_SEEN(b3); \
|
||||
|
@ -167,13 +166,13 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define _EMIT4_DISP(op, disp) \
|
||||
({ \
|
||||
unsigned int __disp = (disp) & 0xfff; \
|
||||
_EMIT4(op | __disp); \
|
||||
_EMIT4((op) | __disp); \
|
||||
})
|
||||
|
||||
#define EMIT4_DISP(op, b1, b2, disp) \
|
||||
({ \
|
||||
_EMIT4_DISP(op | reg_high(b1) << 16 | \
|
||||
reg_high(b2) << 8, disp); \
|
||||
_EMIT4_DISP((op) | reg_high(b1) << 16 | \
|
||||
reg_high(b2) << 8, (disp)); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
@ -181,21 +180,27 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define EMIT4_IMM(op, b1, imm) \
|
||||
({ \
|
||||
unsigned int __imm = (imm) & 0xffff; \
|
||||
_EMIT4(op | reg_high(b1) << 16 | __imm); \
|
||||
_EMIT4((op) | reg_high(b1) << 16 | __imm); \
|
||||
REG_SET_SEEN(b1); \
|
||||
})
|
||||
|
||||
#define EMIT4_PCREL(op, pcrel) \
|
||||
({ \
|
||||
long __pcrel = ((pcrel) >> 1) & 0xffff; \
|
||||
_EMIT4(op | __pcrel); \
|
||||
_EMIT4((op) | __pcrel); \
|
||||
})
|
||||
|
||||
#define EMIT4_PCREL_RIC(op, mask, target) \
|
||||
({ \
|
||||
int __rel = ((target) - jit->prg) / 2; \
|
||||
_EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \
|
||||
})
|
||||
|
||||
#define _EMIT6(op1, op2) \
|
||||
({ \
|
||||
if (jit->prg_buf) { \
|
||||
*(u32 *) (jit->prg_buf + jit->prg) = op1; \
|
||||
*(u16 *) (jit->prg_buf + jit->prg + 4) = op2; \
|
||||
*(u32 *) (jit->prg_buf + jit->prg) = (op1); \
|
||||
*(u16 *) (jit->prg_buf + jit->prg + 4) = (op2); \
|
||||
} \
|
||||
jit->prg += 6; \
|
||||
})
|
||||
|
@ -203,20 +208,20 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define _EMIT6_DISP(op1, op2, disp) \
|
||||
({ \
|
||||
unsigned int __disp = (disp) & 0xfff; \
|
||||
_EMIT6(op1 | __disp, op2); \
|
||||
_EMIT6((op1) | __disp, op2); \
|
||||
})
|
||||
|
||||
#define _EMIT6_DISP_LH(op1, op2, disp) \
|
||||
({ \
|
||||
u32 _disp = (u32) disp; \
|
||||
u32 _disp = (u32) (disp); \
|
||||
unsigned int __disp_h = _disp & 0xff000; \
|
||||
unsigned int __disp_l = _disp & 0x00fff; \
|
||||
_EMIT6(op1 | __disp_l, op2 | __disp_h >> 4); \
|
||||
_EMIT6((op1) | __disp_l, (op2) | __disp_h >> 4); \
|
||||
})
|
||||
|
||||
#define EMIT6_DISP_LH(op1, op2, b1, b2, b3, disp) \
|
||||
({ \
|
||||
_EMIT6_DISP_LH(op1 | reg(b1, b2) << 16 | \
|
||||
_EMIT6_DISP_LH((op1) | reg(b1, b2) << 16 | \
|
||||
reg_high(b3) << 8, op2, disp); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
|
@ -226,8 +231,8 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define EMIT6_PCREL_LABEL(op1, op2, b1, b2, label, mask) \
|
||||
({ \
|
||||
int rel = (jit->labels[label] - jit->prg) >> 1; \
|
||||
_EMIT6(op1 | reg(b1, b2) << 16 | (rel & 0xffff), \
|
||||
op2 | mask << 12); \
|
||||
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \
|
||||
(op2) | (mask) << 12); \
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
@ -235,66 +240,81 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
#define EMIT6_PCREL_IMM_LABEL(op1, op2, b1, imm, label, mask) \
|
||||
({ \
|
||||
int rel = (jit->labels[label] - jit->prg) >> 1; \
|
||||
_EMIT6(op1 | (reg_high(b1) | mask) << 16 | \
|
||||
(rel & 0xffff), op2 | (imm & 0xff) << 8); \
|
||||
_EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \
|
||||
(rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \
|
||||
REG_SET_SEEN(b1); \
|
||||
BUILD_BUG_ON(((unsigned long) imm) > 0xff); \
|
||||
BUILD_BUG_ON(((unsigned long) (imm)) > 0xff); \
|
||||
})
|
||||
|
||||
#define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \
|
||||
({ \
|
||||
/* Branch instruction needs 6 bytes */ \
|
||||
int rel = (addrs[i + off + 1] - (addrs[i + 1] - 6)) / 2;\
|
||||
_EMIT6(op1 | reg(b1, b2) << 16 | (rel & 0xffff), op2 | mask); \
|
||||
int rel = (addrs[(i) + (off) + 1] - (addrs[(i) + 1] - 6)) / 2;\
|
||||
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
||||
#define EMIT6_PCREL_RILB(op, b, target) \
|
||||
({ \
|
||||
int rel = (target - jit->prg) / 2; \
|
||||
_EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
_EMIT6((op) | reg_high(b) << 16 | rel >> 16, rel & 0xffff);\
|
||||
REG_SET_SEEN(b); \
|
||||
})
|
||||
|
||||
#define EMIT6_PCREL_RIL(op, target) \
|
||||
({ \
|
||||
int rel = (target - jit->prg) / 2; \
|
||||
_EMIT6(op | rel >> 16, rel & 0xffff); \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
_EMIT6((op) | rel >> 16, rel & 0xffff); \
|
||||
})
|
||||
|
||||
#define EMIT6_PCREL_RILC(op, mask, target) \
|
||||
({ \
|
||||
EMIT6_PCREL_RIL((op) | (mask) << 20, (target)); \
|
||||
})
|
||||
|
||||
#define _EMIT6_IMM(op, imm) \
|
||||
({ \
|
||||
unsigned int __imm = (imm); \
|
||||
_EMIT6(op | (__imm >> 16), __imm & 0xffff); \
|
||||
_EMIT6((op) | (__imm >> 16), __imm & 0xffff); \
|
||||
})
|
||||
|
||||
#define EMIT6_IMM(op, b1, imm) \
|
||||
({ \
|
||||
_EMIT6_IMM(op | reg_high(b1) << 16, imm); \
|
||||
_EMIT6_IMM((op) | reg_high(b1) << 16, imm); \
|
||||
REG_SET_SEEN(b1); \
|
||||
})
|
||||
|
||||
#define _EMIT_CONST_U32(val) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = jit->lit32; \
|
||||
if (jit->prg_buf) \
|
||||
*(u32 *)(jit->prg_buf + jit->lit32) = (u32)(val);\
|
||||
jit->lit32 += 4; \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define EMIT_CONST_U32(val) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = jit->lit - jit->base_ip; \
|
||||
jit->seen |= SEEN_LITERAL; \
|
||||
_EMIT_CONST_U32(val) - jit->base_ip; \
|
||||
})
|
||||
|
||||
#define _EMIT_CONST_U64(val) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = jit->lit64; \
|
||||
if (jit->prg_buf) \
|
||||
*(u32 *) (jit->prg_buf + jit->lit) = (u32) val; \
|
||||
jit->lit += 4; \
|
||||
*(u64 *)(jit->prg_buf + jit->lit64) = (u64)(val);\
|
||||
jit->lit64 += 8; \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define EMIT_CONST_U64(val) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = jit->lit - jit->base_ip; \
|
||||
jit->seen |= SEEN_LITERAL; \
|
||||
if (jit->prg_buf) \
|
||||
*(u64 *) (jit->prg_buf + jit->lit) = (u64) val; \
|
||||
jit->lit += 8; \
|
||||
ret; \
|
||||
_EMIT_CONST_U64(val) - jit->base_ip; \
|
||||
})
|
||||
|
||||
#define EMIT_ZERO(b1) \
|
||||
|
@ -306,6 +326,67 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
|||
} \
|
||||
})
|
||||
|
||||
/*
|
||||
* Return whether this is the first pass. The first pass is special, since we
|
||||
* don't know any sizes yet, and thus must be conservative.
|
||||
*/
|
||||
static bool is_first_pass(struct bpf_jit *jit)
|
||||
{
|
||||
return jit->size == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether this is the code generation pass. The code generation pass is
|
||||
* special, since we should change as little as possible.
|
||||
*/
|
||||
static bool is_codegen_pass(struct bpf_jit *jit)
|
||||
{
|
||||
return jit->prg_buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether "rel" can be encoded as a short PC-relative offset
|
||||
*/
|
||||
static bool is_valid_rel(int rel)
|
||||
{
|
||||
return rel >= -65536 && rel <= 65534;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether "off" can be reached using a short PC-relative offset
|
||||
*/
|
||||
static bool can_use_rel(struct bpf_jit *jit, int off)
|
||||
{
|
||||
return is_valid_rel(off - jit->prg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether given displacement can be encoded using
|
||||
* Long-Displacement Facility
|
||||
*/
|
||||
static bool is_valid_ldisp(int disp)
|
||||
{
|
||||
return disp >= -524288 && disp <= 524287;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the next 32-bit literal pool entry can be referenced using
|
||||
* Long-Displacement Facility
|
||||
*/
|
||||
static bool can_use_ldisp_for_lit32(struct bpf_jit *jit)
|
||||
{
|
||||
return is_valid_ldisp(jit->lit32 - jit->base_ip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the next 64-bit literal pool entry can be referenced using
|
||||
* Long-Displacement Facility
|
||||
*/
|
||||
static bool can_use_ldisp_for_lit64(struct bpf_jit *jit)
|
||||
{
|
||||
return is_valid_ldisp(jit->lit64 - jit->base_ip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill whole space with illegal instructions
|
||||
*/
|
||||
|
@ -383,9 +464,18 @@ static int get_end(struct bpf_jit *jit, int start)
|
|||
*/
|
||||
static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
|
||||
{
|
||||
|
||||
const int last = 15, save_restore_size = 6;
|
||||
int re = 6, rs;
|
||||
|
||||
if (is_first_pass(jit)) {
|
||||
/*
|
||||
* We don't know yet which registers are used. Reserve space
|
||||
* conservatively.
|
||||
*/
|
||||
jit->prg += (last - re + 1) * save_restore_size;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
rs = get_start(jit, re);
|
||||
if (!rs)
|
||||
|
@ -396,7 +486,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth)
|
|||
else
|
||||
restore_regs(jit, rs, re, stack_depth);
|
||||
re++;
|
||||
} while (re <= 15);
|
||||
} while (re <= last);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -420,21 +510,28 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
|
|||
/* Save registers */
|
||||
save_restore_regs(jit, REGS_SAVE, stack_depth);
|
||||
/* Setup literal pool */
|
||||
if (jit->seen & SEEN_LITERAL) {
|
||||
/* basr %r13,0 */
|
||||
EMIT2(0x0d00, REG_L, REG_0);
|
||||
jit->base_ip = jit->prg;
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_LITERAL)) {
|
||||
if (!is_first_pass(jit) &&
|
||||
is_valid_ldisp(jit->size - (jit->prg + 2))) {
|
||||
/* basr %l,0 */
|
||||
EMIT2(0x0d00, REG_L, REG_0);
|
||||
jit->base_ip = jit->prg;
|
||||
} else {
|
||||
/* larl %l,lit32_start */
|
||||
EMIT6_PCREL_RILB(0xc0000000, REG_L, jit->lit32_start);
|
||||
jit->base_ip = jit->lit32_start;
|
||||
}
|
||||
}
|
||||
/* Setup stack and backchain */
|
||||
if (jit->seen & SEEN_STACK) {
|
||||
if (jit->seen & SEEN_FUNC)
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
|
||||
/* lgr %w1,%r15 (backchain) */
|
||||
EMIT4(0xb9040000, REG_W1, REG_15);
|
||||
/* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
|
||||
EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
|
||||
/* aghi %r15,-STK_OFF */
|
||||
EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth));
|
||||
if (jit->seen & SEEN_FUNC)
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
|
||||
/* stg %w1,152(%r15) (backchain) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
|
||||
REG_15, 152);
|
||||
|
@ -446,12 +543,6 @@ static void bpf_jit_prologue(struct bpf_jit *jit, u32 stack_depth)
|
|||
*/
|
||||
static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
|
||||
{
|
||||
/* Return 0 */
|
||||
if (jit->seen & SEEN_RET0) {
|
||||
jit->ret0_ip = jit->prg;
|
||||
/* lghi %b0,0 */
|
||||
EMIT4_IMM(0xa7090000, BPF_REG_0, 0);
|
||||
}
|
||||
jit->exit_ip = jit->prg;
|
||||
/* Load exit code: lgr %r2,%b0 */
|
||||
EMIT4(0xb9040000, REG_2, BPF_REG_0);
|
||||
|
@ -476,7 +567,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
|
|||
_EMIT2(0x07fe);
|
||||
|
||||
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable &&
|
||||
(jit->seen & SEEN_FUNC)) {
|
||||
(is_first_pass(jit) || (jit->seen & SEEN_FUNC))) {
|
||||
jit->r1_thunk_ip = jit->prg;
|
||||
/* Generate __s390_indirect_jump_r1 thunk */
|
||||
if (test_facility(35)) {
|
||||
|
@ -506,16 +597,14 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
int i, bool extra_pass)
|
||||
{
|
||||
struct bpf_insn *insn = &fp->insnsi[i];
|
||||
int jmp_off, last, insn_count = 1;
|
||||
u32 dst_reg = insn->dst_reg;
|
||||
u32 src_reg = insn->src_reg;
|
||||
int last, insn_count = 1;
|
||||
u32 *addrs = jit->addrs;
|
||||
s32 imm = insn->imm;
|
||||
s16 off = insn->off;
|
||||
unsigned int mask;
|
||||
|
||||
if (dst_reg == BPF_REG_AX || src_reg == BPF_REG_AX)
|
||||
jit->seen |= SEEN_REG_AX;
|
||||
switch (insn->code) {
|
||||
/*
|
||||
* BPF_MOV
|
||||
|
@ -549,9 +638,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
u64 imm64;
|
||||
|
||||
imm64 = (u64)(u32) insn[0].imm | ((u64)(u32) insn[1].imm) << 32;
|
||||
/* lg %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0004, dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm64));
|
||||
/* lgrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, dst_reg, _EMIT_CONST_U64(imm64));
|
||||
insn_count = 2;
|
||||
break;
|
||||
}
|
||||
|
@ -680,9 +768,18 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4_IMM(0xa7080000, REG_W0, 0);
|
||||
/* lr %w1,%dst */
|
||||
EMIT2(0x1800, REG_W1, dst_reg);
|
||||
/* dl %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U32(imm));
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit32(jit)) {
|
||||
/* dl %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0097, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U32(imm));
|
||||
} else {
|
||||
/* lgfrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc40c0000, dst_reg,
|
||||
_EMIT_CONST_U32(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlr %w0,%dst */
|
||||
EMIT4(0xb9970000, REG_W0, dst_reg);
|
||||
}
|
||||
/* llgfr %dst,%rc */
|
||||
EMIT4(0xb9160000, dst_reg, rc_reg);
|
||||
if (insn_is_zext(&insn[1]))
|
||||
|
@ -704,9 +801,18 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4_IMM(0xa7090000, REG_W0, 0);
|
||||
/* lgr %w1,%dst */
|
||||
EMIT4(0xb9040000, REG_W1, dst_reg);
|
||||
/* dlg %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
|
||||
/* dlg %w0,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0087, REG_W0, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
} else {
|
||||
/* lgrl %dst,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, dst_reg,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* dlgr %w0,%dst */
|
||||
EMIT4(0xb9870000, REG_W0, dst_reg);
|
||||
}
|
||||
/* lgr %dst,%rc */
|
||||
EMIT4(0xb9040000, dst_reg, rc_reg);
|
||||
break;
|
||||
|
@ -729,9 +835,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT_ZERO(dst_reg);
|
||||
break;
|
||||
case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */
|
||||
/* ng %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0080, dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
|
||||
/* ng %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0080,
|
||||
dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
} else {
|
||||
/* lgrl %w0,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* ngr %dst,%w0 */
|
||||
EMIT4(0xb9800000, dst_reg, REG_W0);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
* BPF_OR
|
||||
|
@ -751,9 +867,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT_ZERO(dst_reg);
|
||||
break;
|
||||
case BPF_ALU64 | BPF_OR | BPF_K: /* dst = dst | imm */
|
||||
/* og %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0081, dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
|
||||
/* og %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0081,
|
||||
dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
} else {
|
||||
/* lgrl %w0,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* ogr %dst,%w0 */
|
||||
EMIT4(0xb9810000, dst_reg, REG_W0);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
* BPF_XOR
|
||||
|
@ -775,9 +901,19 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT_ZERO(dst_reg);
|
||||
break;
|
||||
case BPF_ALU64 | BPF_XOR | BPF_K: /* dst = dst ^ imm */
|
||||
/* xg %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0082, dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
if (!is_first_pass(jit) && can_use_ldisp_for_lit64(jit)) {
|
||||
/* xg %dst,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0082,
|
||||
dst_reg, REG_0, REG_L,
|
||||
EMIT_CONST_U64(imm));
|
||||
} else {
|
||||
/* lgrl %w0,imm */
|
||||
EMIT6_PCREL_RILB(0xc4080000, REG_W0,
|
||||
_EMIT_CONST_U64(imm));
|
||||
jit->seen |= SEEN_LITERAL;
|
||||
/* xgr %dst,%w0 */
|
||||
EMIT4(0xb9820000, dst_reg, REG_W0);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
* BPF_LSH
|
||||
|
@ -1023,9 +1159,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
|
||||
REG_SET_SEEN(BPF_REG_5);
|
||||
jit->seen |= SEEN_FUNC;
|
||||
/* lg %w1,<d(imm)>(%l) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L,
|
||||
EMIT_CONST_U64(func));
|
||||
/* lgrl %w1,func */
|
||||
EMIT6_PCREL_RILB(0xc4080000, REG_W1, _EMIT_CONST_U64(func));
|
||||
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable) {
|
||||
/* brasl %r14,__s390_indirect_jump_r1 */
|
||||
EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
|
||||
|
@ -1054,9 +1189,17 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
/* llgf %w1,map.max_entries(%b2) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0016, REG_W1, REG_0, BPF_REG_2,
|
||||
offsetof(struct bpf_array, map.max_entries));
|
||||
/* clrj %b3,%w1,0xa,label0: if (u32)%b3 >= (u32)%w1 goto out */
|
||||
EMIT6_PCREL_LABEL(0xec000000, 0x0077, BPF_REG_3,
|
||||
REG_W1, 0, 0xa);
|
||||
/* if ((u32)%b3 >= (u32)%w1) goto out; */
|
||||
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
|
||||
/* clrj %b3,%w1,0xa,label0 */
|
||||
EMIT6_PCREL_LABEL(0xec000000, 0x0077, BPF_REG_3,
|
||||
REG_W1, 0, 0xa);
|
||||
} else {
|
||||
/* clr %b3,%w1 */
|
||||
EMIT2(0x1500, BPF_REG_3, REG_W1);
|
||||
/* brcl 0xa,label0 */
|
||||
EMIT6_PCREL_RILC(0xc0040000, 0xa, jit->labels[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt++ > MAX_TAIL_CALL_CNT)
|
||||
|
@ -1071,9 +1214,16 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4_IMM(0xa7080000, REG_W0, 1);
|
||||
/* laal %w1,%w0,off(%r15) */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W1, REG_W0, REG_15, off);
|
||||
/* clij %w1,MAX_TAIL_CALL_CNT,0x2,label0 */
|
||||
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007f, REG_W1,
|
||||
MAX_TAIL_CALL_CNT, 0, 0x2);
|
||||
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
|
||||
/* clij %w1,MAX_TAIL_CALL_CNT,0x2,label0 */
|
||||
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007f, REG_W1,
|
||||
MAX_TAIL_CALL_CNT, 0, 0x2);
|
||||
} else {
|
||||
/* clfi %w1,MAX_TAIL_CALL_CNT */
|
||||
EMIT6_IMM(0xc20f0000, REG_W1, MAX_TAIL_CALL_CNT);
|
||||
/* brcl 0x2,label0 */
|
||||
EMIT6_PCREL_RILC(0xc0040000, 0x2, jit->labels[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* prog = array->ptrs[index];
|
||||
|
@ -1085,11 +1235,16 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4(0xb9160000, REG_1, BPF_REG_3);
|
||||
/* sllg %r1,%r1,3: %r1 *= 8 */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, REG_1, REG_0, 3);
|
||||
/* lg %r1,prog(%b2,%r1) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, BPF_REG_2,
|
||||
/* ltg %r1,prog(%b2,%r1) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0002, REG_1, BPF_REG_2,
|
||||
REG_1, offsetof(struct bpf_array, ptrs));
|
||||
/* clgij %r1,0,0x8,label0 */
|
||||
EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007d, REG_1, 0, 0, 0x8);
|
||||
if (!is_first_pass(jit) && can_use_rel(jit, jit->labels[0])) {
|
||||
/* brc 0x8,label0 */
|
||||
EMIT4_PCREL_RIC(0xa7040000, 0x8, jit->labels[0]);
|
||||
} else {
|
||||
/* brcl 0x8,label0 */
|
||||
EMIT6_PCREL_RILC(0xc0040000, 0x8, jit->labels[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore registers before calling function
|
||||
|
@ -1110,7 +1265,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
break;
|
||||
case BPF_JMP | BPF_EXIT: /* return b0 */
|
||||
last = (i == fp->len - 1) ? 1 : 0;
|
||||
if (last && !(jit->seen & SEEN_RET0))
|
||||
if (last)
|
||||
break;
|
||||
/* j <exit> */
|
||||
EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
|
||||
|
@ -1246,36 +1401,83 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
goto branch_oc;
|
||||
branch_ks:
|
||||
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
|
||||
/* lgfi %w1,imm (load sign extend imm) */
|
||||
EMIT6_IMM(0xc0010000, REG_W1, imm);
|
||||
/* crj or cgrj %dst,%w1,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
|
||||
dst_reg, REG_W1, i, off, mask);
|
||||
/* cfi or cgfi %dst,imm */
|
||||
EMIT6_IMM(is_jmp32 ? 0xc20d0000 : 0xc20c0000,
|
||||
dst_reg, imm);
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
/* brc mask,off */
|
||||
EMIT4_PCREL_RIC(0xa7040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
} else {
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
}
|
||||
break;
|
||||
branch_ku:
|
||||
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
|
||||
/* lgfi %w1,imm (load sign extend imm) */
|
||||
EMIT6_IMM(0xc0010000, REG_W1, imm);
|
||||
/* clrj or clgrj %dst,%w1,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
|
||||
dst_reg, REG_W1, i, off, mask);
|
||||
/* clfi or clgfi %dst,imm */
|
||||
EMIT6_IMM(is_jmp32 ? 0xc20f0000 : 0xc20e0000,
|
||||
dst_reg, imm);
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
/* brc mask,off */
|
||||
EMIT4_PCREL_RIC(0xa7040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
} else {
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
}
|
||||
break;
|
||||
branch_xs:
|
||||
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
|
||||
/* crj or cgrj %dst,%src,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
|
||||
dst_reg, src_reg, i, off, mask);
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
/* crj or cgrj %dst,%src,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
|
||||
dst_reg, src_reg, i, off, mask);
|
||||
} else {
|
||||
/* cr or cgr %dst,%src */
|
||||
if (is_jmp32)
|
||||
EMIT2(0x1900, dst_reg, src_reg);
|
||||
else
|
||||
EMIT4(0xb9200000, dst_reg, src_reg);
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
}
|
||||
break;
|
||||
branch_xu:
|
||||
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
|
||||
/* clrj or clgrj %dst,%src,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
|
||||
dst_reg, src_reg, i, off, mask);
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
/* clrj or clgrj %dst,%src,mask,off */
|
||||
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
|
||||
dst_reg, src_reg, i, off, mask);
|
||||
} else {
|
||||
/* clr or clgr %dst,%src */
|
||||
if (is_jmp32)
|
||||
EMIT2(0x1500, dst_reg, src_reg);
|
||||
else
|
||||
EMIT4(0xb9210000, dst_reg, src_reg);
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
}
|
||||
break;
|
||||
branch_oc:
|
||||
/* brc mask,jmp_off (branch instruction needs 4 bytes) */
|
||||
jmp_off = addrs[i + off + 1] - (addrs[i + 1] - 4);
|
||||
EMIT4_PCREL(0xa7040000 | mask << 8, jmp_off);
|
||||
if (!is_first_pass(jit) &&
|
||||
can_use_rel(jit, addrs[i + off + 1])) {
|
||||
/* brc mask,off */
|
||||
EMIT4_PCREL_RIC(0xa7040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
} else {
|
||||
/* brcl mask,off */
|
||||
EMIT6_PCREL_RILC(0xc0040000,
|
||||
mask >> 12, addrs[i + off + 1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: /* too complex, give up */
|
||||
|
@ -1285,29 +1487,68 @@ branch_oc:
|
|||
return insn_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether new i-th instruction address does not violate any invariant
|
||||
*/
|
||||
static bool bpf_is_new_addr_sane(struct bpf_jit *jit, int i)
|
||||
{
|
||||
/* On the first pass anything goes */
|
||||
if (is_first_pass(jit))
|
||||
return true;
|
||||
|
||||
/* The codegen pass must not change anything */
|
||||
if (is_codegen_pass(jit))
|
||||
return jit->addrs[i] == jit->prg;
|
||||
|
||||
/* Passes in between must not increase code size */
|
||||
return jit->addrs[i] >= jit->prg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the address of i-th instruction
|
||||
*/
|
||||
static int bpf_set_addr(struct bpf_jit *jit, int i)
|
||||
{
|
||||
if (!bpf_is_new_addr_sane(jit, i))
|
||||
return -1;
|
||||
jit->addrs[i] = jit->prg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile eBPF program into s390x code
|
||||
*/
|
||||
static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
bool extra_pass)
|
||||
{
|
||||
int i, insn_count;
|
||||
int i, insn_count, lit32_size, lit64_size;
|
||||
|
||||
jit->lit = jit->lit_start;
|
||||
jit->lit32 = jit->lit32_start;
|
||||
jit->lit64 = jit->lit64_start;
|
||||
jit->prg = 0;
|
||||
|
||||
bpf_jit_prologue(jit, fp->aux->stack_depth);
|
||||
if (bpf_set_addr(jit, 0) < 0)
|
||||
return -1;
|
||||
for (i = 0; i < fp->len; i += insn_count) {
|
||||
insn_count = bpf_jit_insn(jit, fp, i, extra_pass);
|
||||
if (insn_count < 0)
|
||||
return -1;
|
||||
/* Next instruction address */
|
||||
jit->addrs[i + insn_count] = jit->prg;
|
||||
if (bpf_set_addr(jit, i + insn_count) < 0)
|
||||
return -1;
|
||||
}
|
||||
bpf_jit_epilogue(jit, fp->aux->stack_depth);
|
||||
|
||||
jit->lit_start = jit->prg;
|
||||
jit->size = jit->lit;
|
||||
lit32_size = jit->lit32 - jit->lit32_start;
|
||||
lit64_size = jit->lit64 - jit->lit64_start;
|
||||
jit->lit32_start = jit->prg;
|
||||
if (lit32_size)
|
||||
jit->lit32_start = ALIGN(jit->lit32_start, 4);
|
||||
jit->lit64_start = jit->lit32_start + lit32_size;
|
||||
if (lit64_size)
|
||||
jit->lit64_start = ALIGN(jit->lit64_start, 8);
|
||||
jit->size = jit->lit64_start + lit64_size;
|
||||
jit->size_prg = jit->prg;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1369,7 +1610,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
|
|||
}
|
||||
|
||||
memset(&jit, 0, sizeof(jit));
|
||||
jit.addrs = kcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
|
||||
jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
|
||||
if (jit.addrs == NULL) {
|
||||
fp = orig_fp;
|
||||
goto out;
|
||||
|
@ -1388,12 +1629,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
|
|||
/*
|
||||
* Final pass: Allocate and generate program
|
||||
*/
|
||||
if (jit.size >= BPF_SIZE_MAX) {
|
||||
fp = orig_fp;
|
||||
goto free_addrs;
|
||||
}
|
||||
|
||||
header = bpf_jit_binary_alloc(jit.size, &jit.prg_buf, 2, jit_fill_hole);
|
||||
header = bpf_jit_binary_alloc(jit.size, &jit.prg_buf, 8, jit_fill_hole);
|
||||
if (!header) {
|
||||
fp = orig_fp;
|
||||
goto free_addrs;
|
||||
|
@ -1422,7 +1658,7 @@ skip_init_ctx:
|
|||
if (!fp->is_func || extra_pass) {
|
||||
bpf_prog_fill_jited_linfo(fp, jit.addrs + 1);
|
||||
free_addrs:
|
||||
kfree(jit.addrs);
|
||||
kvfree(jit.addrs);
|
||||
kfree(jit_data);
|
||||
fp->aux->jit_data = NULL;
|
||||
}
|
||||
|
|
|
@ -26,10 +26,11 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
|
|||
#define POKE_MAX_OPCODE_SIZE 5
|
||||
|
||||
struct text_poke_loc {
|
||||
void *detour;
|
||||
void *addr;
|
||||
size_t len;
|
||||
const char opcode[POKE_MAX_OPCODE_SIZE];
|
||||
int len;
|
||||
s32 rel32;
|
||||
u8 opcode;
|
||||
const u8 text[POKE_MAX_OPCODE_SIZE];
|
||||
};
|
||||
|
||||
extern void text_poke_early(void *addr, const void *opcode, size_t len);
|
||||
|
@ -51,8 +52,10 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
|
|||
extern void *text_poke(void *addr, const void *opcode, size_t len);
|
||||
extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
|
||||
extern int poke_int3_handler(struct pt_regs *regs);
|
||||
extern void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
|
||||
extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
|
||||
extern void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries);
|
||||
extern void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
|
||||
const void *opcode, size_t len, const void *emulate);
|
||||
extern int after_bootmem;
|
||||
extern __ro_after_init struct mm_struct *poking_mm;
|
||||
extern __ro_after_init unsigned long poking_addr;
|
||||
|
@ -63,8 +66,17 @@ static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip)
|
|||
regs->ip = ip;
|
||||
}
|
||||
|
||||
#define INT3_INSN_SIZE 1
|
||||
#define CALL_INSN_SIZE 5
|
||||
#define INT3_INSN_SIZE 1
|
||||
#define INT3_INSN_OPCODE 0xCC
|
||||
|
||||
#define CALL_INSN_SIZE 5
|
||||
#define CALL_INSN_OPCODE 0xE8
|
||||
|
||||
#define JMP32_INSN_SIZE 5
|
||||
#define JMP32_INSN_OPCODE 0xE9
|
||||
|
||||
#define JMP8_INSN_SIZE 2
|
||||
#define JMP8_INSN_OPCODE 0xEB
|
||||
|
||||
static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val)
|
||||
{
|
||||
|
|
|
@ -956,16 +956,15 @@ NOKPROBE_SYMBOL(patch_cmp);
|
|||
int poke_int3_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct text_poke_loc *tp;
|
||||
unsigned char int3 = 0xcc;
|
||||
void *ip;
|
||||
|
||||
/*
|
||||
* Having observed our INT3 instruction, we now must observe
|
||||
* bp_patching.nr_entries.
|
||||
*
|
||||
* nr_entries != 0 INT3
|
||||
* WMB RMB
|
||||
* write INT3 if (nr_entries)
|
||||
* nr_entries != 0 INT3
|
||||
* WMB RMB
|
||||
* write INT3 if (nr_entries)
|
||||
*
|
||||
* Idem for other elements in bp_patching.
|
||||
*/
|
||||
|
@ -978,9 +977,9 @@ int poke_int3_handler(struct pt_regs *regs)
|
|||
return 0;
|
||||
|
||||
/*
|
||||
* Discount the sizeof(int3). See text_poke_bp_batch().
|
||||
* Discount the INT3. See text_poke_bp_batch().
|
||||
*/
|
||||
ip = (void *) regs->ip - sizeof(int3);
|
||||
ip = (void *) regs->ip - INT3_INSN_SIZE;
|
||||
|
||||
/*
|
||||
* Skip the binary search if there is a single member in the vector.
|
||||
|
@ -997,8 +996,28 @@ int poke_int3_handler(struct pt_regs *regs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* set up the specified breakpoint detour */
|
||||
regs->ip = (unsigned long) tp->detour;
|
||||
ip += tp->len;
|
||||
|
||||
switch (tp->opcode) {
|
||||
case INT3_INSN_OPCODE:
|
||||
/*
|
||||
* Someone poked an explicit INT3, they'll want to handle it,
|
||||
* do not consume.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
case CALL_INSN_OPCODE:
|
||||
int3_emulate_call(regs, (long)ip + tp->rel32);
|
||||
break;
|
||||
|
||||
case JMP32_INSN_OPCODE:
|
||||
case JMP8_INSN_OPCODE:
|
||||
int3_emulate_jmp(regs, (long)ip + tp->rel32);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1014,7 +1033,7 @@ NOKPROBE_SYMBOL(poke_int3_handler);
|
|||
* synchronization using int3 breakpoint.
|
||||
*
|
||||
* The way it is done:
|
||||
* - For each entry in the vector:
|
||||
* - For each entry in the vector:
|
||||
* - add a int3 trap to the address that will be patched
|
||||
* - sync cores
|
||||
* - For each entry in the vector:
|
||||
|
@ -1027,9 +1046,9 @@ NOKPROBE_SYMBOL(poke_int3_handler);
|
|||
*/
|
||||
void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
|
||||
{
|
||||
int patched_all_but_first = 0;
|
||||
unsigned char int3 = 0xcc;
|
||||
unsigned char int3 = INT3_INSN_OPCODE;
|
||||
unsigned int i;
|
||||
int do_sync;
|
||||
|
||||
lockdep_assert_held(&text_mutex);
|
||||
|
||||
|
@ -1053,16 +1072,16 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
|
|||
/*
|
||||
* Second step: update all but the first byte of the patched range.
|
||||
*/
|
||||
for (i = 0; i < nr_entries; i++) {
|
||||
for (do_sync = 0, i = 0; i < nr_entries; i++) {
|
||||
if (tp[i].len - sizeof(int3) > 0) {
|
||||
text_poke((char *)tp[i].addr + sizeof(int3),
|
||||
(const char *)tp[i].opcode + sizeof(int3),
|
||||
(const char *)tp[i].text + sizeof(int3),
|
||||
tp[i].len - sizeof(int3));
|
||||
patched_all_but_first++;
|
||||
do_sync++;
|
||||
}
|
||||
}
|
||||
|
||||
if (patched_all_but_first) {
|
||||
if (do_sync) {
|
||||
/*
|
||||
* According to Intel, this core syncing is very likely
|
||||
* not necessary and we'd be safe even without it. But
|
||||
|
@ -1075,10 +1094,17 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
|
|||
* Third step: replace the first byte (int3) by the first byte of
|
||||
* replacing opcode.
|
||||
*/
|
||||
for (i = 0; i < nr_entries; i++)
|
||||
text_poke(tp[i].addr, tp[i].opcode, sizeof(int3));
|
||||
for (do_sync = 0, i = 0; i < nr_entries; i++) {
|
||||
if (tp[i].text[0] == INT3_INSN_OPCODE)
|
||||
continue;
|
||||
|
||||
text_poke(tp[i].addr, tp[i].text, sizeof(int3));
|
||||
do_sync++;
|
||||
}
|
||||
|
||||
if (do_sync)
|
||||
on_each_cpu(do_sync_core, NULL, 1);
|
||||
|
||||
on_each_cpu(do_sync_core, NULL, 1);
|
||||
/*
|
||||
* sync_core() implies an smp_mb() and orders this store against
|
||||
* the writing of the new instruction.
|
||||
|
@ -1087,6 +1113,60 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
|
|||
bp_patching.nr_entries = 0;
|
||||
}
|
||||
|
||||
void text_poke_loc_init(struct text_poke_loc *tp, void *addr,
|
||||
const void *opcode, size_t len, const void *emulate)
|
||||
{
|
||||
struct insn insn;
|
||||
|
||||
if (!opcode)
|
||||
opcode = (void *)tp->text;
|
||||
else
|
||||
memcpy((void *)tp->text, opcode, len);
|
||||
|
||||
if (!emulate)
|
||||
emulate = opcode;
|
||||
|
||||
kernel_insn_init(&insn, emulate, MAX_INSN_SIZE);
|
||||
insn_get_length(&insn);
|
||||
|
||||
BUG_ON(!insn_complete(&insn));
|
||||
BUG_ON(len != insn.length);
|
||||
|
||||
tp->addr = addr;
|
||||
tp->len = len;
|
||||
tp->opcode = insn.opcode.bytes[0];
|
||||
|
||||
switch (tp->opcode) {
|
||||
case INT3_INSN_OPCODE:
|
||||
break;
|
||||
|
||||
case CALL_INSN_OPCODE:
|
||||
case JMP32_INSN_OPCODE:
|
||||
case JMP8_INSN_OPCODE:
|
||||
tp->rel32 = insn.immediate.value;
|
||||
break;
|
||||
|
||||
default: /* assume NOP */
|
||||
switch (len) {
|
||||
case 2: /* NOP2 -- emulate as JMP8+0 */
|
||||
BUG_ON(memcmp(emulate, ideal_nops[len], len));
|
||||
tp->opcode = JMP8_INSN_OPCODE;
|
||||
tp->rel32 = 0;
|
||||
break;
|
||||
|
||||
case 5: /* NOP5 -- emulate as JMP32+0 */
|
||||
BUG_ON(memcmp(emulate, ideal_nops[NOP_ATOMIC5], len));
|
||||
tp->opcode = JMP32_INSN_OPCODE;
|
||||
tp->rel32 = 0;
|
||||
break;
|
||||
|
||||
default: /* unknown instruction */
|
||||
BUG();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* text_poke_bp() -- update instructions on live kernel on SMP
|
||||
* @addr: address to patch
|
||||
|
@ -1098,20 +1178,10 @@ void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries)
|
|||
* dynamically allocated memory. This function should be used when it is
|
||||
* not possible to allocate memory.
|
||||
*/
|
||||
void text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
|
||||
void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate)
|
||||
{
|
||||
struct text_poke_loc tp = {
|
||||
.detour = handler,
|
||||
.addr = addr,
|
||||
.len = len,
|
||||
};
|
||||
|
||||
if (len > POKE_MAX_OPCODE_SIZE) {
|
||||
WARN_ONCE(1, "len is larger than %d\n", POKE_MAX_OPCODE_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((void *)tp.opcode, opcode, len);
|
||||
struct text_poke_loc tp;
|
||||
|
||||
text_poke_loc_init(&tp, addr, opcode, len, emulate);
|
||||
text_poke_bp_batch(&tp, 1);
|
||||
}
|
||||
|
|
|
@ -89,8 +89,7 @@ static void __ref __jump_label_transform(struct jump_entry *entry,
|
|||
return;
|
||||
}
|
||||
|
||||
text_poke_bp((void *)jump_entry_code(entry), &code, JUMP_LABEL_NOP_SIZE,
|
||||
(void *)jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
|
||||
text_poke_bp((void *)jump_entry_code(entry), &code, JUMP_LABEL_NOP_SIZE, NULL);
|
||||
}
|
||||
|
||||
void arch_jump_label_transform(struct jump_entry *entry,
|
||||
|
@ -147,11 +146,9 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry,
|
|||
}
|
||||
|
||||
__jump_label_set_jump_code(entry, type,
|
||||
(union jump_code_union *) &tp->opcode, 0);
|
||||
(union jump_code_union *)&tp->text, 0);
|
||||
|
||||
tp->addr = entry_code;
|
||||
tp->detour = entry_code + JUMP_LABEL_NOP_SIZE;
|
||||
tp->len = JUMP_LABEL_NOP_SIZE;
|
||||
text_poke_loc_init(tp, entry_code, NULL, JUMP_LABEL_NOP_SIZE, NULL);
|
||||
|
||||
tp_vec_nr++;
|
||||
|
||||
|
|
|
@ -437,8 +437,7 @@ void arch_optimize_kprobes(struct list_head *oplist)
|
|||
insn_buff[0] = RELATIVEJUMP_OPCODE;
|
||||
*(s32 *)(&insn_buff[1]) = rel;
|
||||
|
||||
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE,
|
||||
op->optinsn.insn);
|
||||
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE, NULL);
|
||||
|
||||
list_del_init(&op->list);
|
||||
}
|
||||
|
@ -448,12 +447,18 @@ void arch_optimize_kprobes(struct list_head *oplist)
|
|||
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
|
||||
{
|
||||
u8 insn_buff[RELATIVEJUMP_SIZE];
|
||||
u8 emulate_buff[RELATIVEJUMP_SIZE];
|
||||
|
||||
/* Set int3 to first byte for kprobes */
|
||||
insn_buff[0] = BREAKPOINT_INSTRUCTION;
|
||||
memcpy(insn_buff + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
|
||||
|
||||
emulate_buff[0] = RELATIVEJUMP_OPCODE;
|
||||
*(s32 *)(&emulate_buff[1]) = (s32)((long)op->optinsn.insn -
|
||||
((long)op->kp.addr + RELATIVEJUMP_SIZE));
|
||||
|
||||
text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE,
|
||||
op->optinsn.insn);
|
||||
emulate_buff);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -13,7 +13,7 @@ CFLAGS_REMOVE_mem_encrypt_identity.o = -pg
|
|||
endif
|
||||
|
||||
obj-y := init.o init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \
|
||||
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o
|
||||
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o maccess.o
|
||||
|
||||
# Make sure __phys_addr has no stackprotector
|
||||
nostackp := $(call cc-option, -fno-stack-protector)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
static __always_inline u64 canonical_address(u64 vaddr, u8 vaddr_bits)
|
||||
{
|
||||
return ((s64)vaddr << (64 - vaddr_bits)) >> (64 - vaddr_bits);
|
||||
}
|
||||
|
||||
static __always_inline bool invalid_probe_range(u64 vaddr)
|
||||
{
|
||||
/*
|
||||
* Range covering the highest possible canonical userspace address
|
||||
* as well as non-canonical address range. For the canonical range
|
||||
* we also need to include the userspace guard page.
|
||||
*/
|
||||
return vaddr < TASK_SIZE_MAX + PAGE_SIZE ||
|
||||
canonical_address(vaddr, boot_cpu_data.x86_virt_bits) != vaddr;
|
||||
}
|
||||
#else
|
||||
static __always_inline bool invalid_probe_range(u64 vaddr)
|
||||
{
|
||||
return vaddr < TASK_SIZE_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
long probe_kernel_read_strict(void *dst, const void *src, size_t size)
|
||||
{
|
||||
if (unlikely(invalid_probe_range((unsigned long)src)))
|
||||
return -EFAULT;
|
||||
|
||||
return __probe_kernel_read(dst, src, size);
|
||||
}
|
||||
|
||||
long strncpy_from_unsafe_strict(char *dst, const void *unsafe_addr, long count)
|
||||
{
|
||||
if (unlikely(invalid_probe_range((unsigned long)unsafe_addr)))
|
||||
return -EFAULT;
|
||||
|
||||
return __strncpy_from_unsafe(dst, unsafe_addr, count);
|
||||
}
|
|
@ -9,9 +9,11 @@
|
|||
#include <linux/filter.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <linux/memory.h>
|
||||
#include <asm/extable.h>
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/text-patching.h>
|
||||
|
||||
static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
|
||||
{
|
||||
|
@ -96,6 +98,7 @@ static int bpf_size_to_x86_bytes(int bpf_size)
|
|||
|
||||
/* Pick a register outside of BPF range for JIT internal work */
|
||||
#define AUX_REG (MAX_BPF_JIT_REG + 1)
|
||||
#define X86_REG_R9 (MAX_BPF_JIT_REG + 2)
|
||||
|
||||
/*
|
||||
* The following table maps BPF registers to x86-64 registers.
|
||||
|
@ -104,8 +107,8 @@ static int bpf_size_to_x86_bytes(int bpf_size)
|
|||
* register in load/store instructions, it always needs an
|
||||
* extra byte of encoding and is callee saved.
|
||||
*
|
||||
* Also x86-64 register R9 is unused. x86-64 register R10 is
|
||||
* used for blinding (if enabled).
|
||||
* x86-64 register R9 is not used by BPF programs, but can be used by BPF
|
||||
* trampoline. x86-64 register R10 is used for blinding (if enabled).
|
||||
*/
|
||||
static const int reg2hex[] = {
|
||||
[BPF_REG_0] = 0, /* RAX */
|
||||
|
@ -121,6 +124,20 @@ static const int reg2hex[] = {
|
|||
[BPF_REG_FP] = 5, /* RBP readonly */
|
||||
[BPF_REG_AX] = 2, /* R10 temp register */
|
||||
[AUX_REG] = 3, /* R11 temp register */
|
||||
[X86_REG_R9] = 1, /* R9 register, 6th function argument */
|
||||
};
|
||||
|
||||
static const int reg2pt_regs[] = {
|
||||
[BPF_REG_0] = offsetof(struct pt_regs, ax),
|
||||
[BPF_REG_1] = offsetof(struct pt_regs, di),
|
||||
[BPF_REG_2] = offsetof(struct pt_regs, si),
|
||||
[BPF_REG_3] = offsetof(struct pt_regs, dx),
|
||||
[BPF_REG_4] = offsetof(struct pt_regs, cx),
|
||||
[BPF_REG_5] = offsetof(struct pt_regs, r8),
|
||||
[BPF_REG_6] = offsetof(struct pt_regs, bx),
|
||||
[BPF_REG_7] = offsetof(struct pt_regs, r13),
|
||||
[BPF_REG_8] = offsetof(struct pt_regs, r14),
|
||||
[BPF_REG_9] = offsetof(struct pt_regs, r15),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -135,6 +152,7 @@ static bool is_ereg(u32 reg)
|
|||
BIT(BPF_REG_7) |
|
||||
BIT(BPF_REG_8) |
|
||||
BIT(BPF_REG_9) |
|
||||
BIT(X86_REG_R9) |
|
||||
BIT(BPF_REG_AX));
|
||||
}
|
||||
|
||||
|
@ -186,7 +204,10 @@ struct jit_context {
|
|||
#define BPF_MAX_INSN_SIZE 128
|
||||
#define BPF_INSN_SAFETY 64
|
||||
|
||||
#define PROLOGUE_SIZE 20
|
||||
/* Number of bytes emit_patch() needs to generate instructions */
|
||||
#define X86_PATCH_SIZE 5
|
||||
|
||||
#define PROLOGUE_SIZE 25
|
||||
|
||||
/*
|
||||
* Emit x86-64 prologue code for BPF program and check its size.
|
||||
|
@ -195,8 +216,13 @@ struct jit_context {
|
|||
static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
int cnt = X86_PATCH_SIZE;
|
||||
|
||||
/* BPF trampoline can be made to work without these nops,
|
||||
* but let's waste 5 bytes for now and optimize later
|
||||
*/
|
||||
memcpy(prog, ideal_nops[NOP_ATOMIC5], cnt);
|
||||
prog += cnt;
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
/* sub rsp, rounded_stack_depth */
|
||||
|
@ -213,6 +239,89 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
|
|||
*pprog = prog;
|
||||
}
|
||||
|
||||
static int emit_patch(u8 **pprog, void *func, void *ip, u8 opcode)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
s64 offset;
|
||||
|
||||
offset = func - (ip + X86_PATCH_SIZE);
|
||||
if (!is_simm32(offset)) {
|
||||
pr_err("Target call %p is out of range\n", func);
|
||||
return -ERANGE;
|
||||
}
|
||||
EMIT1_off32(opcode, offset);
|
||||
*pprog = prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emit_call(u8 **pprog, void *func, void *ip)
|
||||
{
|
||||
return emit_patch(pprog, func, ip, 0xE8);
|
||||
}
|
||||
|
||||
static int emit_jump(u8 **pprog, void *func, void *ip)
|
||||
{
|
||||
return emit_patch(pprog, func, ip, 0xE9);
|
||||
}
|
||||
|
||||
static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
|
||||
void *old_addr, void *new_addr,
|
||||
const bool text_live)
|
||||
{
|
||||
const u8 *nop_insn = ideal_nops[NOP_ATOMIC5];
|
||||
u8 old_insn[X86_PATCH_SIZE];
|
||||
u8 new_insn[X86_PATCH_SIZE];
|
||||
u8 *prog;
|
||||
int ret;
|
||||
|
||||
memcpy(old_insn, nop_insn, X86_PATCH_SIZE);
|
||||
if (old_addr) {
|
||||
prog = old_insn;
|
||||
ret = t == BPF_MOD_CALL ?
|
||||
emit_call(&prog, old_addr, ip) :
|
||||
emit_jump(&prog, old_addr, ip);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(new_insn, nop_insn, X86_PATCH_SIZE);
|
||||
if (new_addr) {
|
||||
prog = new_insn;
|
||||
ret = t == BPF_MOD_CALL ?
|
||||
emit_call(&prog, new_addr, ip) :
|
||||
emit_jump(&prog, new_addr, ip);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = -EBUSY;
|
||||
mutex_lock(&text_mutex);
|
||||
if (memcmp(ip, old_insn, X86_PATCH_SIZE))
|
||||
goto out;
|
||||
if (memcmp(ip, new_insn, X86_PATCH_SIZE)) {
|
||||
if (text_live)
|
||||
text_poke_bp(ip, new_insn, X86_PATCH_SIZE, NULL);
|
||||
else
|
||||
memcpy(ip, new_insn, X86_PATCH_SIZE);
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&text_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
|
||||
void *old_addr, void *new_addr)
|
||||
{
|
||||
if (!is_kernel_text((long)ip) &&
|
||||
!is_bpf_text_address((long)ip))
|
||||
/* BPF poking in modules is not supported */
|
||||
return -EINVAL;
|
||||
|
||||
return __bpf_arch_text_poke(ip, t, old_addr, new_addr, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the following code:
|
||||
*
|
||||
|
@ -227,7 +336,7 @@ static void emit_prologue(u8 **pprog, u32 stack_depth, bool ebpf_from_cbpf)
|
|||
* goto *(prog->bpf_func + prologue_size);
|
||||
* out:
|
||||
*/
|
||||
static void emit_bpf_tail_call(u8 **pprog)
|
||||
static void emit_bpf_tail_call_indirect(u8 **pprog)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int label1, label2, label3;
|
||||
|
@ -294,6 +403,68 @@ static void emit_bpf_tail_call(u8 **pprog)
|
|||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke,
|
||||
u8 **pprog, int addr, u8 *image)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
EMIT2_off32(0x8B, 0x85, -36 - MAX_BPF_STACK); /* mov eax, dword ptr [rbp - 548] */
|
||||
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
|
||||
EMIT2(X86_JA, 14); /* ja out */
|
||||
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
|
||||
EMIT2_off32(0x89, 0x85, -36 - MAX_BPF_STACK); /* mov dword ptr [rbp -548], eax */
|
||||
|
||||
poke->ip = image + (addr - X86_PATCH_SIZE);
|
||||
poke->adj_off = PROLOGUE_SIZE;
|
||||
|
||||
memcpy(prog, ideal_nops[NOP_ATOMIC5], X86_PATCH_SIZE);
|
||||
prog += X86_PATCH_SIZE;
|
||||
/* out: */
|
||||
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_jit_poke_descriptor *poke;
|
||||
struct bpf_array *array;
|
||||
struct bpf_prog *target;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < prog->aux->size_poke_tab; i++) {
|
||||
poke = &prog->aux->poke_tab[i];
|
||||
WARN_ON_ONCE(READ_ONCE(poke->ip_stable));
|
||||
|
||||
if (poke->reason != BPF_POKE_REASON_TAIL_CALL)
|
||||
continue;
|
||||
|
||||
array = container_of(poke->tail_call.map, struct bpf_array, map);
|
||||
mutex_lock(&array->aux->poke_mutex);
|
||||
target = array->ptrs[poke->tail_call.key];
|
||||
if (target) {
|
||||
/* Plain memcpy is used when image is not live yet
|
||||
* and still not locked as read-only. Once poke
|
||||
* location is active (poke->ip_stable), any parallel
|
||||
* bpf_arch_text_poke() might occur still on the
|
||||
* read-write image until we finally locked it as
|
||||
* read-only. Both modifications on the given image
|
||||
* are under text_mutex to avoid interference.
|
||||
*/
|
||||
ret = __bpf_arch_text_poke(poke->ip, BPF_MOD_JUMP, NULL,
|
||||
(u8 *)target->bpf_func +
|
||||
poke->adj_off, false);
|
||||
BUG_ON(ret < 0);
|
||||
}
|
||||
WRITE_ONCE(poke->ip_stable, true);
|
||||
mutex_unlock(&array->aux->poke_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_mov_imm32(u8 **pprog, bool sign_propagate,
|
||||
u32 dst_reg, const u32 imm32)
|
||||
{
|
||||
|
@ -377,6 +548,96 @@ static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
|
|||
*pprog = prog;
|
||||
}
|
||||
|
||||
/* LDX: dst_reg = *(u8*)(src_reg + off) */
|
||||
static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
switch (size) {
|
||||
case BPF_B:
|
||||
/* Emit 'movzx rax, byte ptr [rax + off]' */
|
||||
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
|
||||
break;
|
||||
case BPF_H:
|
||||
/* Emit 'movzx rax, word ptr [rax + off]' */
|
||||
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
|
||||
break;
|
||||
case BPF_W:
|
||||
/* Emit 'mov eax, dword ptr [rax+0x14]' */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
|
||||
else
|
||||
EMIT1(0x8B);
|
||||
break;
|
||||
case BPF_DW:
|
||||
/* Emit 'mov rax, qword ptr [rax+0x14]' */
|
||||
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If insn->off == 0 we can save one extra byte, but
|
||||
* special case of x86 R13 which always needs an offset
|
||||
* is not worth the hassle
|
||||
*/
|
||||
if (is_imm8(off))
|
||||
EMIT2(add_2reg(0x40, src_reg, dst_reg), off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, src_reg, dst_reg), off);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
/* STX: *(u8*)(dst_reg + off) = src_reg */
|
||||
static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0;
|
||||
|
||||
switch (size) {
|
||||
case BPF_B:
|
||||
/* Emit 'mov byte ptr [rax + off], al' */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg) ||
|
||||
/* We have to add extra byte for x86 SIL, DIL regs */
|
||||
src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
|
||||
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
|
||||
else
|
||||
EMIT1(0x88);
|
||||
break;
|
||||
case BPF_H:
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
|
||||
else
|
||||
EMIT2(0x66, 0x89);
|
||||
break;
|
||||
case BPF_W:
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
|
||||
else
|
||||
EMIT1(0x89);
|
||||
break;
|
||||
case BPF_DW:
|
||||
EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
|
||||
break;
|
||||
}
|
||||
if (is_imm8(off))
|
||||
EMIT2(add_2reg(0x40, dst_reg, src_reg), off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), off);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static bool ex_handler_bpf(const struct exception_table_entry *x,
|
||||
struct pt_regs *regs, int trapnr,
|
||||
unsigned long error_code, unsigned long fault_addr)
|
||||
{
|
||||
u32 reg = x->fixup >> 8;
|
||||
|
||||
/* jump over faulting load and clear dest register */
|
||||
*(unsigned long *)((void *)regs + reg) = 0;
|
||||
regs->ip += x->fixup & 0xff;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
int oldproglen, struct jit_context *ctx)
|
||||
{
|
||||
|
@ -384,7 +645,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
|||
int insn_cnt = bpf_prog->len;
|
||||
bool seen_exit = false;
|
||||
u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY];
|
||||
int i, cnt = 0;
|
||||
int i, cnt = 0, excnt = 0;
|
||||
int proglen = 0;
|
||||
u8 *prog = temp;
|
||||
|
||||
|
@ -747,64 +1008,64 @@ st: if (is_imm8(insn->off))
|
|||
|
||||
/* STX: *(u8*)(dst_reg + off) = src_reg */
|
||||
case BPF_STX | BPF_MEM | BPF_B:
|
||||
/* Emit 'mov byte ptr [rax + off], al' */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg) ||
|
||||
/* We have to add extra byte for x86 SIL, DIL regs */
|
||||
src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
|
||||
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
|
||||
else
|
||||
EMIT1(0x88);
|
||||
goto stx;
|
||||
case BPF_STX | BPF_MEM | BPF_H:
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
|
||||
else
|
||||
EMIT2(0x66, 0x89);
|
||||
goto stx;
|
||||
case BPF_STX | BPF_MEM | BPF_W:
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
|
||||
else
|
||||
EMIT1(0x89);
|
||||
goto stx;
|
||||
case BPF_STX | BPF_MEM | BPF_DW:
|
||||
EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
|
||||
stx: if (is_imm8(insn->off))
|
||||
EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
|
||||
insn->off);
|
||||
emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
break;
|
||||
|
||||
/* LDX: dst_reg = *(u8*)(src_reg + off) */
|
||||
case BPF_LDX | BPF_MEM | BPF_B:
|
||||
/* Emit 'movzx rax, byte ptr [rax + off]' */
|
||||
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
|
||||
goto ldx;
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_B:
|
||||
case BPF_LDX | BPF_MEM | BPF_H:
|
||||
/* Emit 'movzx rax, word ptr [rax + off]' */
|
||||
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
|
||||
goto ldx;
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
|
||||
case BPF_LDX | BPF_MEM | BPF_W:
|
||||
/* Emit 'mov eax, dword ptr [rax+0x14]' */
|
||||
if (is_ereg(dst_reg) || is_ereg(src_reg))
|
||||
EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
|
||||
else
|
||||
EMIT1(0x8B);
|
||||
goto ldx;
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
|
||||
case BPF_LDX | BPF_MEM | BPF_DW:
|
||||
/* Emit 'mov rax, qword ptr [rax+0x14]' */
|
||||
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
|
||||
ldx: /*
|
||||
* If insn->off == 0 we can save one extra byte, but
|
||||
* special case of x86 R13 which always needs an offset
|
||||
* is not worth the hassle
|
||||
*/
|
||||
if (is_imm8(insn->off))
|
||||
EMIT2(add_2reg(0x40, src_reg, dst_reg), insn->off);
|
||||
else
|
||||
EMIT1_off32(add_2reg(0x80, src_reg, dst_reg),
|
||||
insn->off);
|
||||
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
|
||||
emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
|
||||
struct exception_table_entry *ex;
|
||||
u8 *_insn = image + proglen;
|
||||
s64 delta;
|
||||
|
||||
if (!bpf_prog->aux->extable)
|
||||
break;
|
||||
|
||||
if (excnt >= bpf_prog->aux->num_exentries) {
|
||||
pr_err("ex gen bug\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
ex = &bpf_prog->aux->extable[excnt++];
|
||||
|
||||
delta = _insn - (u8 *)&ex->insn;
|
||||
if (!is_simm32(delta)) {
|
||||
pr_err("extable->insn doesn't fit into 32-bit\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
ex->insn = delta;
|
||||
|
||||
delta = (u8 *)ex_handler_bpf - (u8 *)&ex->handler;
|
||||
if (!is_simm32(delta)) {
|
||||
pr_err("extable->handler doesn't fit into 32-bit\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
ex->handler = delta;
|
||||
|
||||
if (dst_reg > BPF_REG_9) {
|
||||
pr_err("verifier error\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
/*
|
||||
* Compute size of x86 insn and its target dest x86 register.
|
||||
* ex_handler_bpf() will use lower 8 bits to adjust
|
||||
* pt_regs->ip to jump over this x86 instruction
|
||||
* and upper bits to figure out which pt_regs to zero out.
|
||||
* End result: x86 insn "mov rbx, qword ptr [rax+0x14]"
|
||||
* of 4 bytes will be ignored and rbx will be zero inited.
|
||||
*/
|
||||
ex->fixup = (prog - temp) | (reg2pt_regs[dst_reg] << 8);
|
||||
}
|
||||
break;
|
||||
|
||||
/* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
|
||||
|
@ -827,17 +1088,16 @@ xadd: if (is_imm8(insn->off))
|
|||
/* call */
|
||||
case BPF_JMP | BPF_CALL:
|
||||
func = (u8 *) __bpf_call_base + imm32;
|
||||
jmp_offset = func - (image + addrs[i]);
|
||||
if (!imm32 || !is_simm32(jmp_offset)) {
|
||||
pr_err("unsupported BPF func %d addr %p image %p\n",
|
||||
imm32, func, image);
|
||||
if (!imm32 || emit_call(&prog, func, image + addrs[i - 1]))
|
||||
return -EINVAL;
|
||||
}
|
||||
EMIT1_off32(0xE8, jmp_offset);
|
||||
break;
|
||||
|
||||
case BPF_JMP | BPF_TAIL_CALL:
|
||||
emit_bpf_tail_call(&prog);
|
||||
if (imm32)
|
||||
emit_bpf_tail_call_direct(&bpf_prog->aux->poke_tab[imm32 - 1],
|
||||
&prog, addrs[i], image);
|
||||
else
|
||||
emit_bpf_tail_call_indirect(&prog);
|
||||
break;
|
||||
|
||||
/* cond jump */
|
||||
|
@ -909,6 +1169,16 @@ xadd: if (is_imm8(insn->off))
|
|||
case BPF_JMP32 | BPF_JSLT | BPF_K:
|
||||
case BPF_JMP32 | BPF_JSGE | BPF_K:
|
||||
case BPF_JMP32 | BPF_JSLE | BPF_K:
|
||||
/* test dst_reg, dst_reg to save one extra byte */
|
||||
if (imm32 == 0) {
|
||||
if (BPF_CLASS(insn->code) == BPF_JMP)
|
||||
EMIT1(add_2mod(0x48, dst_reg, dst_reg));
|
||||
else if (is_ereg(dst_reg))
|
||||
EMIT1(add_2mod(0x40, dst_reg, dst_reg));
|
||||
EMIT2(0x85, add_2reg(0xC0, dst_reg, dst_reg));
|
||||
goto emit_cond_jmp;
|
||||
}
|
||||
|
||||
/* cmp dst_reg, imm8/32 */
|
||||
if (BPF_CLASS(insn->code) == BPF_JMP)
|
||||
EMIT1(add_1mod(0x48, dst_reg));
|
||||
|
@ -1048,9 +1318,218 @@ emit_jmp:
|
|||
addrs[i] = proglen;
|
||||
prog = temp;
|
||||
}
|
||||
|
||||
if (image && excnt != bpf_prog->aux->num_exentries) {
|
||||
pr_err("extable is not populated\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
return proglen;
|
||||
}
|
||||
|
||||
static void save_regs(struct btf_func_model *m, u8 **prog, int nr_args,
|
||||
int stack_size)
|
||||
{
|
||||
int i;
|
||||
/* Store function arguments to stack.
|
||||
* For a function that accepts two pointers the sequence will be:
|
||||
* mov QWORD PTR [rbp-0x10],rdi
|
||||
* mov QWORD PTR [rbp-0x8],rsi
|
||||
*/
|
||||
for (i = 0; i < min(nr_args, 6); i++)
|
||||
emit_stx(prog, bytes_to_bpf_size(m->arg_size[i]),
|
||||
BPF_REG_FP,
|
||||
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
|
||||
-(stack_size - i * 8));
|
||||
}
|
||||
|
||||
static void restore_regs(struct btf_func_model *m, u8 **prog, int nr_args,
|
||||
int stack_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Restore function arguments from stack.
|
||||
* For a function that accepts two pointers the sequence will be:
|
||||
* EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10]
|
||||
* EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8]
|
||||
*/
|
||||
for (i = 0; i < min(nr_args, 6); i++)
|
||||
emit_ldx(prog, bytes_to_bpf_size(m->arg_size[i]),
|
||||
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
|
||||
BPF_REG_FP,
|
||||
-(stack_size - i * 8));
|
||||
}
|
||||
|
||||
static int invoke_bpf(struct btf_func_model *m, u8 **pprog,
|
||||
struct bpf_prog **progs, int prog_cnt, int stack_size)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
int cnt = 0, i;
|
||||
|
||||
for (i = 0; i < prog_cnt; i++) {
|
||||
if (emit_call(&prog, __bpf_prog_enter, prog))
|
||||
return -EINVAL;
|
||||
/* remember prog start time returned by __bpf_prog_enter */
|
||||
emit_mov_reg(&prog, true, BPF_REG_6, BPF_REG_0);
|
||||
|
||||
/* arg1: lea rdi, [rbp - stack_size] */
|
||||
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
|
||||
/* arg2: progs[i]->insnsi for interpreter */
|
||||
if (!progs[i]->jited)
|
||||
emit_mov_imm64(&prog, BPF_REG_2,
|
||||
(long) progs[i]->insnsi >> 32,
|
||||
(u32) (long) progs[i]->insnsi);
|
||||
/* call JITed bpf program or interpreter */
|
||||
if (emit_call(&prog, progs[i]->bpf_func, prog))
|
||||
return -EINVAL;
|
||||
|
||||
/* arg1: mov rdi, progs[i] */
|
||||
emit_mov_imm64(&prog, BPF_REG_1, (long) progs[i] >> 32,
|
||||
(u32) (long) progs[i]);
|
||||
/* arg2: mov rsi, rbx <- start time in nsec */
|
||||
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
|
||||
if (emit_call(&prog, __bpf_prog_exit, prog))
|
||||
return -EINVAL;
|
||||
}
|
||||
*pprog = prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Example:
|
||||
* __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
|
||||
* its 'struct btf_func_model' will be nr_args=2
|
||||
* The assembly code when eth_type_trans is executing after trampoline:
|
||||
*
|
||||
* push rbp
|
||||
* mov rbp, rsp
|
||||
* sub rsp, 16 // space for skb and dev
|
||||
* push rbx // temp regs to pass start time
|
||||
* mov qword ptr [rbp - 16], rdi // save skb pointer to stack
|
||||
* mov qword ptr [rbp - 8], rsi // save dev pointer to stack
|
||||
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
|
||||
* mov rbx, rax // remember start time in bpf stats are enabled
|
||||
* lea rdi, [rbp - 16] // R1==ctx of bpf prog
|
||||
* call addr_of_jited_FENTRY_prog
|
||||
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
|
||||
* mov rsi, rbx // prog start time
|
||||
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
|
||||
* mov rdi, qword ptr [rbp - 16] // restore skb pointer from stack
|
||||
* mov rsi, qword ptr [rbp - 8] // restore dev pointer from stack
|
||||
* pop rbx
|
||||
* leave
|
||||
* ret
|
||||
*
|
||||
* eth_type_trans has 5 byte nop at the beginning. These 5 bytes will be
|
||||
* replaced with 'call generated_bpf_trampoline'. When it returns
|
||||
* eth_type_trans will continue executing with original skb and dev pointers.
|
||||
*
|
||||
* The assembly code when eth_type_trans is called from trampoline:
|
||||
*
|
||||
* push rbp
|
||||
* mov rbp, rsp
|
||||
* sub rsp, 24 // space for skb, dev, return value
|
||||
* push rbx // temp regs to pass start time
|
||||
* mov qword ptr [rbp - 24], rdi // save skb pointer to stack
|
||||
* mov qword ptr [rbp - 16], rsi // save dev pointer to stack
|
||||
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
|
||||
* mov rbx, rax // remember start time if bpf stats are enabled
|
||||
* lea rdi, [rbp - 24] // R1==ctx of bpf prog
|
||||
* call addr_of_jited_FENTRY_prog // bpf prog can access skb and dev
|
||||
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
|
||||
* mov rsi, rbx // prog start time
|
||||
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
|
||||
* mov rdi, qword ptr [rbp - 24] // restore skb pointer from stack
|
||||
* mov rsi, qword ptr [rbp - 16] // restore dev pointer from stack
|
||||
* call eth_type_trans+5 // execute body of eth_type_trans
|
||||
* mov qword ptr [rbp - 8], rax // save return value
|
||||
* call __bpf_prog_enter // rcu_read_lock and preempt_disable
|
||||
* mov rbx, rax // remember start time in bpf stats are enabled
|
||||
* lea rdi, [rbp - 24] // R1==ctx of bpf prog
|
||||
* call addr_of_jited_FEXIT_prog // bpf prog can access skb, dev, return value
|
||||
* movabsq rdi, 64bit_addr_of_struct_bpf_prog // unused if bpf stats are off
|
||||
* mov rsi, rbx // prog start time
|
||||
* call __bpf_prog_exit // rcu_read_unlock, preempt_enable and stats math
|
||||
* mov rax, qword ptr [rbp - 8] // restore eth_type_trans's return value
|
||||
* pop rbx
|
||||
* leave
|
||||
* add rsp, 8 // skip eth_type_trans's frame
|
||||
* ret // return to its caller
|
||||
*/
|
||||
int arch_prepare_bpf_trampoline(void *image, struct btf_func_model *m, u32 flags,
|
||||
struct bpf_prog **fentry_progs, int fentry_cnt,
|
||||
struct bpf_prog **fexit_progs, int fexit_cnt,
|
||||
void *orig_call)
|
||||
{
|
||||
int cnt = 0, nr_args = m->nr_args;
|
||||
int stack_size = nr_args * 8;
|
||||
u8 *prog;
|
||||
|
||||
/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
|
||||
if (nr_args > 6)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if ((flags & BPF_TRAMP_F_RESTORE_REGS) &&
|
||||
(flags & BPF_TRAMP_F_SKIP_FRAME))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG)
|
||||
stack_size += 8; /* room for return value of orig_call */
|
||||
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
/* skip patched call instruction and point orig_call to actual
|
||||
* body of the kernel function.
|
||||
*/
|
||||
orig_call += X86_PATCH_SIZE;
|
||||
|
||||
prog = image;
|
||||
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */
|
||||
EMIT1(0x53); /* push rbx */
|
||||
|
||||
save_regs(m, &prog, nr_args, stack_size);
|
||||
|
||||
if (fentry_cnt)
|
||||
if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
if (fentry_cnt)
|
||||
restore_regs(m, &prog, nr_args, stack_size);
|
||||
|
||||
/* call original function */
|
||||
if (emit_call(&prog, orig_call, prog))
|
||||
return -EINVAL;
|
||||
/* remember return value in a stack for bpf prog to access */
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
|
||||
}
|
||||
|
||||
if (fexit_cnt)
|
||||
if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
||||
restore_regs(m, &prog, nr_args, stack_size);
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG)
|
||||
/* restore original return value back into RAX */
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
|
||||
|
||||
EMIT1(0x5B); /* pop rbx */
|
||||
EMIT1(0xC9); /* leave */
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
/* skip our return address and return to parent */
|
||||
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */
|
||||
EMIT1(0xC3); /* ret */
|
||||
/* One half of the page has active running trampoline.
|
||||
* Another half is an area for next trampoline.
|
||||
* Make sure the trampoline generation logic doesn't overflow.
|
||||
*/
|
||||
if (WARN_ON_ONCE(prog - (u8 *)image > PAGE_SIZE / 2 - BPF_INSN_SAFETY))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct x64_jit_data {
|
||||
struct bpf_binary_header *header;
|
||||
int *addrs;
|
||||
|
@ -1148,12 +1627,24 @@ out_image:
|
|||
break;
|
||||
}
|
||||
if (proglen == oldproglen) {
|
||||
header = bpf_jit_binary_alloc(proglen, &image,
|
||||
1, jit_fill_hole);
|
||||
/*
|
||||
* The number of entries in extable is the number of BPF_LDX
|
||||
* insns that access kernel memory via "pointer to BTF type".
|
||||
* The verifier changed their opcode from LDX|MEM|size
|
||||
* to LDX|PROBE_MEM|size to make JITing easier.
|
||||
*/
|
||||
u32 align = __alignof__(struct exception_table_entry);
|
||||
u32 extable_size = prog->aux->num_exentries *
|
||||
sizeof(struct exception_table_entry);
|
||||
|
||||
/* allocate module memory for x86 insns and extable */
|
||||
header = bpf_jit_binary_alloc(roundup(proglen, align) + extable_size,
|
||||
&image, align, jit_fill_hole);
|
||||
if (!header) {
|
||||
prog = orig_prog;
|
||||
goto out_addrs;
|
||||
}
|
||||
prog->aux->extable = (void *) image + roundup(proglen, align);
|
||||
}
|
||||
oldproglen = proglen;
|
||||
cond_resched();
|
||||
|
@ -1164,6 +1655,7 @@ out_image:
|
|||
|
||||
if (image) {
|
||||
if (!prog->is_func || extra_pass) {
|
||||
bpf_tail_call_direct_fixup(prog);
|
||||
bpf_jit_binary_lock_ro(header);
|
||||
} else {
|
||||
jit_data->addrs = addrs;
|
||||
|
|
|
@ -1070,7 +1070,7 @@ static int fs_open(struct atm_vcc *atm_vcc)
|
|||
RC_FLAGS_BFPS_BFP * bfp |
|
||||
RC_FLAGS_RXBM_PSB, 0, 0);
|
||||
break;
|
||||
};
|
||||
}
|
||||
if (IS_FS50 (dev)) {
|
||||
submit_command (dev, &dev->hp_txq,
|
||||
QE_CMD_REG_WR | QE_CMD_IMM_INQ,
|
||||
|
|
|
@ -233,8 +233,10 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
|
|||
|
||||
switch (bus->chipinfo.id) {
|
||||
case BCMA_CHIP_ID_BCM4313:
|
||||
/* enable 12 mA drive strenth for 4313 and set chipControl
|
||||
register bit 1 */
|
||||
/*
|
||||
* enable 12 mA drive strenth for 4313 and set chipControl
|
||||
* register bit 1
|
||||
*/
|
||||
bcma_chipco_chipctl_maskset(cc, 0,
|
||||
~BCMA_CCTRL_4313_12MA_LED_DRIVE,
|
||||
BCMA_CCTRL_4313_12MA_LED_DRIVE);
|
||||
|
@ -246,8 +248,10 @@ static void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
|
|||
break;
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43421:
|
||||
/* enable 12 mA drive strenth for 43224 and set chipControl
|
||||
register bit 15 */
|
||||
/*
|
||||
* enable 12 mA drive strenth for 43224 and set chipControl
|
||||
* register bit 15
|
||||
*/
|
||||
if (bus->chipinfo.rev == 0) {
|
||||
bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL,
|
||||
~BCMA_CCTRL_43224_GPIO_TOGGLE,
|
||||
|
@ -500,8 +504,10 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
|||
case BCMA_CHIP_ID_BCM53572:
|
||||
/* 5357[ab]0, 43236[ab]0, and 6362b0 */
|
||||
|
||||
/* BCM5357 needs to touch PLL1_PLLCTL[02],
|
||||
so offset PLL0_PLLCTL[02] by 6 */
|
||||
/*
|
||||
* BCM5357 needs to touch PLL1_PLLCTL[02],
|
||||
* so offset PLL0_PLLCTL[02] by 6
|
||||
*/
|
||||
phypll_offset = (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM4749 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0;
|
||||
|
@ -619,8 +625,10 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
|
|||
case BCMA_CHIP_ID_BCM43228:
|
||||
case BCMA_CHIP_ID_BCM43428:
|
||||
/* LCNXN */
|
||||
/* PLL Settings for spur avoidance on/off mode,
|
||||
no on2 support for 43228A0 */
|
||||
/*
|
||||
* PLL Settings for spur avoidance on/off mode,
|
||||
* no on2 support for 43228A0
|
||||
*/
|
||||
if (spuravoid == 1) {
|
||||
bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0,
|
||||
0x01100014);
|
||||
|
|
|
@ -380,17 +380,6 @@ config BT_ATH3K
|
|||
Say Y here to compile support for "Atheros firmware download driver"
|
||||
into the kernel or say M to compile it as module (ath3k).
|
||||
|
||||
config BT_WILINK
|
||||
tristate "Texas Instruments WiLink7 driver"
|
||||
depends on TI_ST
|
||||
help
|
||||
This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
|
||||
combo devices. This makes use of shared transport line discipline
|
||||
core driver to communicate with the BT core of the combo chip.
|
||||
|
||||
Say Y here to compile support for Texas Instrument's WiLink7 driver
|
||||
into the kernel or say M to compile it as module (btwilink).
|
||||
|
||||
config BT_MTKSDIO
|
||||
tristate "MediaTek HCI SDIO driver"
|
||||
depends on MMC
|
||||
|
|
|
@ -19,7 +19,6 @@ obj-$(CONFIG_BT_INTEL) += btintel.o
|
|||
obj-$(CONFIG_BT_ATH3K) += ath3k.o
|
||||
obj-$(CONFIG_BT_MRVL) += btmrvl.o
|
||||
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
|
||||
obj-$(CONFIG_BT_WILINK) += btwilink.o
|
||||
obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o
|
||||
obj-$(CONFIG_BT_MTKUART) += btmtkuart.o
|
||||
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
|
||||
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
|
||||
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
|
||||
#define BDADDR_BCM4334B0 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb0, 0x34, 0x43}})
|
||||
#define BDADDR_BCM4345C5 (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0xc5, 0x45, 0x43}})
|
||||
#define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}})
|
||||
|
||||
|
@ -74,6 +75,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
|||
!bacmp(&bda->bdaddr, BDADDR_BCM2076B1) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4330B1) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
|
||||
|
@ -326,6 +328,7 @@ struct bcm_subver_table {
|
|||
|
||||
static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
||||
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
||||
{ 0x410d, "BCM4334B0" }, /* 002.001.013 */
|
||||
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
||||
{ 0x4204, "BCM2076B1" }, /* 002.002.004 */
|
||||
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
||||
|
@ -339,6 +342,7 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
|||
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
|
||||
{ 0x4217, "BCM4329B1" }, /* 002.002.023 */
|
||||
{ 0x6106, "BCM4359C0" }, /* 003.001.006 */
|
||||
{ 0x4106, "BCM4335A0" }, /* 002.001.006 */
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -440,6 +444,12 @@ int btbcm_finalize(struct hci_dev *hdev)
|
|||
|
||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||
|
||||
/* Some devices ship with the controller default address.
|
||||
* Allow the bootloader to set a valid address through the
|
||||
* device tree.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_finalize);
|
||||
|
|
|
@ -709,6 +709,51 @@ done:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_download_firmware);
|
||||
|
||||
void btintel_reset_to_bootloader(struct hci_dev *hdev)
|
||||
{
|
||||
struct intel_reset params;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Send Intel Reset command. This will result in
|
||||
* re-enumeration of BT controller.
|
||||
*
|
||||
* Intel Reset parameter description:
|
||||
* reset_type : 0x00 (Soft reset),
|
||||
* 0x01 (Hard reset)
|
||||
* patch_enable : 0x00 (Do not enable),
|
||||
* 0x01 (Enable)
|
||||
* ddc_reload : 0x00 (Do not reload),
|
||||
* 0x01 (Reload)
|
||||
* boot_option: 0x00 (Current image),
|
||||
* 0x01 (Specified boot address)
|
||||
* boot_param: Boot address
|
||||
*
|
||||
*/
|
||||
params.reset_type = 0x01;
|
||||
params.patch_enable = 0x01;
|
||||
params.ddc_reload = 0x01;
|
||||
params.boot_option = 0x00;
|
||||
params.boot_param = cpu_to_le32(0x00000000);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params),
|
||||
¶ms, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "FW download error recovery failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
bt_dev_info(hdev, "Intel reset sent to retry FW download");
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Current Intel BT controllers(ThP/JfP) hold the USB reset
|
||||
* lines for 2ms when it receives Intel Reset in bootloader mode.
|
||||
* Whereas, the upcoming Intel BT controllers will hold USB reset
|
||||
* for 150ms. To keep the delay generic, 150ms is chosen here.
|
||||
*/
|
||||
msleep(150);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_reset_to_bootloader);
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
|
|
|
@ -87,6 +87,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
|
|||
struct intel_boot_params *params);
|
||||
int btintel_download_firmware(struct hci_dev *dev, const struct firmware *fw,
|
||||
u32 *boot_param);
|
||||
void btintel_reset_to_bootloader(struct hci_dev *hdev);
|
||||
#else
|
||||
|
||||
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||
|
@ -181,4 +182,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
|
|||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void btintel_reset_to_bootloader(struct hci_dev *hdev)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -57,6 +57,7 @@ static const struct sdio_device_id btmtksdio_table[] = {
|
|||
.driver_data = (kernel_ulong_t)&mt7668_data },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
|
||||
|
||||
#define MTK_REG_CHLPCR 0x4 /* W1S */
|
||||
#define C_INT_EN_SET BIT(0)
|
||||
|
|
|
@ -14,19 +14,33 @@
|
|||
|
||||
#define VERSION "0.1"
|
||||
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
struct rome_version *ver;
|
||||
struct qca_btsoc_version *ver;
|
||||
char cmd;
|
||||
int err = 0;
|
||||
u8 event_type = HCI_EV_VENDOR;
|
||||
u8 rlen = sizeof(*edl) + sizeof(*ver);
|
||||
u8 rtype = EDL_APP_VER_RES_EVT;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA Version Request");
|
||||
|
||||
/* Unlike other SoC's sending version command response as payload to
|
||||
* VSE event. WCN3991 sends version command response as a payload to
|
||||
* command complete event.
|
||||
*/
|
||||
if (soc_type == QCA_WCN3991) {
|
||||
event_type = 0;
|
||||
rlen += 1;
|
||||
rtype = EDL_PATCH_VER_REQ_CMD;
|
||||
}
|
||||
|
||||
cmd = EDL_PATCH_VER_REQ_CMD;
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
|
||||
&cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
&cmd, event_type, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
|
||||
|
@ -34,7 +48,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*edl) + sizeof(*ver)) {
|
||||
if (skb->len != rlen) {
|
||||
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
|
@ -48,18 +62,21 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
|||
}
|
||||
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
|
||||
edl->rtype != EDL_APP_VER_RES_EVT) {
|
||||
edl->rtype != rtype) {
|
||||
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
|
||||
edl->rtype);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ver = (struct rome_version *)(edl->data);
|
||||
if (soc_type == QCA_WCN3991)
|
||||
memmove(&edl->data, &edl->data[1], sizeof(*ver));
|
||||
|
||||
ver = (struct qca_btsoc_version *)(edl->data);
|
||||
|
||||
BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
|
||||
BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver));
|
||||
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver));
|
||||
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rom_ver));
|
||||
BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
|
||||
|
||||
/* QCA chipset version can be decided by patch and SoC
|
||||
|
@ -67,7 +84,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
|||
* and lower 2 bytes from patch will be used.
|
||||
*/
|
||||
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
|
||||
(le16_to_cpu(ver->rome_ver) & 0x0000ffff);
|
||||
(le16_to_cpu(ver->rom_ver) & 0x0000ffff);
|
||||
if (*soc_version == 0)
|
||||
err = -EILSEQ;
|
||||
|
||||
|
@ -121,7 +138,7 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
|
||||
|
||||
static void qca_tlv_check_data(struct rome_config *config,
|
||||
static void qca_tlv_check_data(struct qca_fw_config *config,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
const u8 *data;
|
||||
|
@ -140,8 +157,8 @@ static void qca_tlv_check_data(struct rome_config *config,
|
|||
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
|
||||
BT_DBG("Length\t\t : %d bytes", length);
|
||||
|
||||
config->dnld_mode = ROME_SKIP_EVT_NONE;
|
||||
config->dnld_type = ROME_SKIP_EVT_NONE;
|
||||
config->dnld_mode = QCA_SKIP_EVT_NONE;
|
||||
config->dnld_type = QCA_SKIP_EVT_NONE;
|
||||
|
||||
switch (config->type) {
|
||||
case TLV_TYPE_PATCH:
|
||||
|
@ -223,31 +240,45 @@ static void qca_tlv_check_data(struct rome_config *config,
|
|||
}
|
||||
|
||||
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
const u8 *data, enum rome_tlv_dnld_mode mode)
|
||||
const u8 *data, enum qca_tlv_dnld_mode mode,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
struct tlv_seg_resp *tlv_resp;
|
||||
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
|
||||
int err = 0;
|
||||
u8 event_type = HCI_EV_VENDOR;
|
||||
u8 rlen = (sizeof(*edl) + sizeof(*tlv_resp));
|
||||
u8 rtype = EDL_TVL_DNLD_RES_EVT;
|
||||
|
||||
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
|
||||
cmd[1] = seg_size;
|
||||
memcpy(cmd + 2, data, seg_size);
|
||||
|
||||
if (mode == ROME_SKIP_EVT_VSE_CC || mode == ROME_SKIP_EVT_VSE)
|
||||
if (mode == QCA_SKIP_EVT_VSE_CC || mode == QCA_SKIP_EVT_VSE)
|
||||
return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
|
||||
cmd);
|
||||
|
||||
/* Unlike other SoC's sending version command response as payload to
|
||||
* VSE event. WCN3991 sends version command response as a payload to
|
||||
* command complete event.
|
||||
*/
|
||||
if (soc_type == QCA_WCN3991) {
|
||||
event_type = 0;
|
||||
rlen = sizeof(*edl);
|
||||
rtype = EDL_PATCH_TLV_REQ_CMD;
|
||||
}
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
|
||||
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
event_type, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
|
||||
if (skb->len != rlen) {
|
||||
bt_dev_err(hdev, "QCA TLV response size mismatch");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
|
@ -260,13 +291,19 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
|||
goto out;
|
||||
}
|
||||
|
||||
tlv_resp = (struct tlv_seg_resp *)(edl->data);
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT || edl->rtype != rtype) {
|
||||
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x",
|
||||
edl->cresp, edl->rtype);
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
|
||||
edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) {
|
||||
if (soc_type == QCA_WCN3991)
|
||||
goto out;
|
||||
|
||||
tlv_resp = (struct tlv_seg_resp *)(edl->data);
|
||||
if (tlv_resp->result) {
|
||||
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
|
||||
edl->cresp, edl->rtype, tlv_resp->result);
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -301,7 +338,8 @@ static int qca_inject_cmd_complete_event(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
static int qca_download_firmware(struct hci_dev *hdev,
|
||||
struct rome_config *config)
|
||||
struct qca_fw_config *config,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const u8 *segment;
|
||||
|
@ -328,10 +366,10 @@ static int qca_download_firmware(struct hci_dev *hdev,
|
|||
remain -= segsize;
|
||||
/* The last segment is always acked regardless download mode */
|
||||
if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
|
||||
config->dnld_mode = ROME_SKIP_EVT_NONE;
|
||||
config->dnld_mode = QCA_SKIP_EVT_NONE;
|
||||
|
||||
ret = qca_tlv_send_segment(hdev, segsize, segment,
|
||||
config->dnld_mode);
|
||||
config->dnld_mode, soc_type);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -344,8 +382,8 @@ static int qca_download_firmware(struct hci_dev *hdev,
|
|||
* decrease the BT in initialization time. Here we will inject a command
|
||||
* complete event to avoid a command timeout error message.
|
||||
*/
|
||||
if (config->dnld_type == ROME_SKIP_EVT_VSE_CC ||
|
||||
config->dnld_type == ROME_SKIP_EVT_VSE)
|
||||
if (config->dnld_type == QCA_SKIP_EVT_VSE_CC ||
|
||||
config->dnld_type == QCA_SKIP_EVT_VSE)
|
||||
ret = qca_inject_cmd_complete_event(hdev);
|
||||
|
||||
out:
|
||||
|
@ -382,7 +420,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
|||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
struct rome_config config;
|
||||
struct qca_fw_config config;
|
||||
int err;
|
||||
u8 rom_ver = 0;
|
||||
|
||||
|
@ -405,7 +443,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
|||
"qca/rampatch_%08x.bin", soc_ver);
|
||||
}
|
||||
|
||||
err = qca_download_firmware(hdev, &config);
|
||||
err = qca_download_firmware(hdev, &config, soc_type);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
|
||||
return err;
|
||||
|
@ -426,7 +464,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
|||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/nvm_%08x.bin", soc_ver);
|
||||
|
||||
err = qca_download_firmware(hdev, &config);
|
||||
err = qca_download_firmware(hdev, &config, soc_type);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
|
||||
return err;
|
||||
|
|
|
@ -56,24 +56,24 @@ enum qca_baudrate {
|
|||
QCA_BAUDRATE_RESERVED
|
||||
};
|
||||
|
||||
enum rome_tlv_dnld_mode {
|
||||
ROME_SKIP_EVT_NONE,
|
||||
ROME_SKIP_EVT_VSE,
|
||||
ROME_SKIP_EVT_CC,
|
||||
ROME_SKIP_EVT_VSE_CC
|
||||
enum qca_tlv_dnld_mode {
|
||||
QCA_SKIP_EVT_NONE,
|
||||
QCA_SKIP_EVT_VSE,
|
||||
QCA_SKIP_EVT_CC,
|
||||
QCA_SKIP_EVT_VSE_CC
|
||||
};
|
||||
|
||||
enum rome_tlv_type {
|
||||
enum qca_tlv_type {
|
||||
TLV_TYPE_PATCH = 1,
|
||||
TLV_TYPE_NVM
|
||||
};
|
||||
|
||||
struct rome_config {
|
||||
struct qca_fw_config {
|
||||
u8 type;
|
||||
char fwname[64];
|
||||
uint8_t user_baud_rate;
|
||||
enum rome_tlv_dnld_mode dnld_mode;
|
||||
enum rome_tlv_dnld_mode dnld_type;
|
||||
enum qca_tlv_dnld_mode dnld_mode;
|
||||
enum qca_tlv_dnld_mode dnld_type;
|
||||
};
|
||||
|
||||
struct edl_event_hdr {
|
||||
|
@ -82,10 +82,10 @@ struct edl_event_hdr {
|
|||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
struct rome_version {
|
||||
struct qca_btsoc_version {
|
||||
__le32 product_id;
|
||||
__le16 patch_ver;
|
||||
__le16 rome_ver;
|
||||
__le16 rom_ver;
|
||||
__le32 soc_id;
|
||||
} __packed;
|
||||
|
||||
|
@ -125,6 +125,7 @@ enum qca_btsoc_type {
|
|||
QCA_AR3002,
|
||||
QCA_ROME,
|
||||
QCA_WCN3990,
|
||||
QCA_WCN3991,
|
||||
QCA_WCN3998,
|
||||
};
|
||||
|
||||
|
@ -134,12 +135,14 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
|||
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
const char *firmware_name);
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
enum qca_btsoc_type);
|
||||
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int qca_send_pre_shutdown_cmd(struct hci_dev *hdev);
|
||||
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
|
||||
{
|
||||
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3998;
|
||||
return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3991 ||
|
||||
soc_type == QCA_WCN3998;
|
||||
}
|
||||
#else
|
||||
|
||||
|
@ -155,7 +158,8 @@ static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
||||
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
|
||||
enum qca_btsoc_type)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -418,7 +418,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
|
|||
if (IS_ERR(skb)) {
|
||||
rtl_dev_err(hdev, "download fw command failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
ret = -PTR_ERR(skb);
|
||||
ret = PTR_ERR(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -778,7 +778,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
|
|||
rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)",
|
||||
le16_to_cpu(entry->offset), entry->len);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
i += sizeof(*entry) + entry->len;
|
||||
}
|
||||
|
|
|
@ -1200,7 +1200,7 @@ static int btusb_open(struct hci_dev *hdev)
|
|||
if (data->setup_on_usb) {
|
||||
err = data->setup_on_usb(hdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
data->intf->needs_remote_wakeup = 1;
|
||||
|
@ -1239,6 +1239,7 @@ done:
|
|||
|
||||
failed:
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
setup_fail:
|
||||
usb_autopm_put_interface(data->intf);
|
||||
return err;
|
||||
}
|
||||
|
@ -2182,8 +2183,11 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
|||
* loaded.
|
||||
*/
|
||||
err = btintel_read_version(hdev, &ver);
|
||||
if (err)
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Intel Read version failed (%d)", err);
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The hardware platform number has a fixed value of 0x37 and
|
||||
* for now only accept this single value.
|
||||
|
@ -2326,9 +2330,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
|||
|
||||
/* Start firmware downloading and get boot parameter */
|
||||
err = btintel_download_firmware(hdev, fw, &boot_param);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
/* When FW download fails, send Intel Reset to retry
|
||||
* FW download.
|
||||
*/
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
goto done;
|
||||
|
||||
}
|
||||
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
|
||||
|
||||
bt_dev_info(hdev, "Waiting for firmware download to complete");
|
||||
|
@ -2355,6 +2363,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
|||
if (err) {
|
||||
bt_dev_err(hdev, "Firmware loading timeout");
|
||||
err = -ETIMEDOUT;
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -2381,8 +2390,11 @@ done:
|
|||
set_bit(BTUSB_BOOTING, &data->flags);
|
||||
|
||||
err = btintel_send_intel_reset(hdev, boot_param);
|
||||
if (err)
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Intel Soft Reset failed (%d)", err);
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The bootloader will not indicate when the device is ready. This
|
||||
* is done by the operational firmware sending bootup notification.
|
||||
|
@ -2404,6 +2416,7 @@ done:
|
|||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Device boot timeout");
|
||||
btintel_reset_to_bootloader(hdev);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
@ -2432,6 +2445,13 @@ done:
|
|||
*/
|
||||
btintel_set_event_mask(hdev, false);
|
||||
|
||||
/* Read the Intel version information after loading the FW */
|
||||
err = btintel_read_version(hdev, &ver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
btintel_version_info(hdev, &ver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2489,8 +2509,6 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_MTK
|
||||
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
|
||||
|
@ -3051,7 +3069,6 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev)
|
|||
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
|
||||
|
@ -3411,7 +3428,6 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||
static inline int __set_diag_interface(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
@ -3498,7 +3514,6 @@ static int btusb_bcm_set_diag(struct hci_dev *hdev, bool enable)
|
|||
|
||||
return submit_or_queue_tx_urb(hdev, urb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static irqreturn_t btusb_oob_wake_handler(int irq, void *priv)
|
||||
|
@ -3724,8 +3739,8 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
if (id->driver_info & BTUSB_BCM92035)
|
||||
hdev->setup = btusb_setup_bcm92035;
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) &&
|
||||
(id->driver_info & BTUSB_BCM_PATCHRAM)) {
|
||||
hdev->manufacturer = 15;
|
||||
hdev->setup = btbcm_setup_patchram;
|
||||
hdev->set_diag = btusb_bcm_set_diag;
|
||||
|
@ -3735,7 +3750,8 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_BCM_APPLE) {
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) &&
|
||||
(id->driver_info & BTUSB_BCM_APPLE)) {
|
||||
hdev->manufacturer = 15;
|
||||
hdev->setup = btbcm_setup_apple;
|
||||
hdev->set_diag = btusb_bcm_set_diag;
|
||||
|
@ -3743,7 +3759,6 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
/* Broadcom LM_DIAG Interface numbers are hardcoded */
|
||||
data->diag = usb_ifnum_to_if(data->udev, ifnum_base + 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL) {
|
||||
hdev->manufacturer = 2;
|
||||
|
@ -3774,14 +3789,13 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
if (id->driver_info & BTUSB_MARVELL)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_MTK
|
||||
if (id->driver_info & BTUSB_MEDIATEK) {
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) &&
|
||||
(id->driver_info & BTUSB_MEDIATEK)) {
|
||||
hdev->setup = btusb_mtk_setup;
|
||||
hdev->shutdown = btusb_mtk_shutdown;
|
||||
hdev->manufacturer = 70;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (id->driver_info & BTUSB_SWAVE) {
|
||||
set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
|
||||
|
@ -3807,8 +3821,8 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
btusb_check_needs_reset_resume(intf);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_RTL
|
||||
if (id->driver_info & BTUSB_REALTEK) {
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
|
||||
(id->driver_info & BTUSB_REALTEK)) {
|
||||
hdev->setup = btrtl_setup_realtek;
|
||||
hdev->shutdown = btrtl_shutdown_realtek;
|
||||
hdev->cmd_timeout = btusb_rtl_cmd_timeout;
|
||||
|
@ -3819,7 +3833,6 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
*/
|
||||
set_bit(BTUSB_WAKEUP_DISABLE, &data->flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (id->driver_info & BTUSB_AMP) {
|
||||
/* AMP controllers do not support SCO packets */
|
||||
|
@ -3887,15 +3900,13 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
goto out_free_dev;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_BCM
|
||||
if (data->diag) {
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
|
||||
if (!usb_driver_claim_interface(&btusb_driver,
|
||||
data->diag, data))
|
||||
__set_diag_interface(hdev);
|
||||
else
|
||||
data->diag = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (enable_autosuspend)
|
||||
usb_enable_autosuspend(data->udev);
|
||||
|
|
|
@ -1,337 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Texas Instrument's Bluetooth Driver For Shared Transport.
|
||||
*
|
||||
* Bluetooth Driver acts as interface between HCI core and
|
||||
* TI Shared Transport Layer.
|
||||
*
|
||||
* Copyright (C) 2009-2010 Texas Instruments
|
||||
* Author: Raja Mani <raja_mani@ti.com>
|
||||
* Pavan Savoy <pavan_savoy@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
|
||||
#include <linux/ti_wilink_st.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/* Bluetooth Driver Version */
|
||||
#define VERSION "1.0"
|
||||
#define MAX_BT_CHNL_IDS 3
|
||||
|
||||
/* Number of seconds to wait for registration completion
|
||||
* when ST returns PENDING status.
|
||||
*/
|
||||
#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
|
||||
|
||||
/**
|
||||
* struct ti_st - driver operation structure
|
||||
* @hdev: hci device pointer which binds to bt driver
|
||||
* @reg_status: ST registration callback status
|
||||
* @st_write: write function provided by the ST driver
|
||||
* to be used by the driver during send_frame.
|
||||
* @wait_reg_completion - completion sync between ti_st_open
|
||||
* and st_reg_completion_cb.
|
||||
*/
|
||||
struct ti_st {
|
||||
struct hci_dev *hdev;
|
||||
int reg_status;
|
||||
long (*st_write) (struct sk_buff *);
|
||||
struct completion wait_reg_completion;
|
||||
};
|
||||
|
||||
/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
|
||||
static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
|
||||
{
|
||||
struct hci_dev *hdev = hst->hdev;
|
||||
|
||||
/* Update HCI stat counters */
|
||||
switch (pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------- Interfaces to Shared Transport ------ */
|
||||
|
||||
/* Called by ST layer to indicate protocol registration completion
|
||||
* status.ti_st_open() function will wait for signal from this
|
||||
* API when st_register() function returns ST_PENDING.
|
||||
*/
|
||||
static void st_reg_completion_cb(void *priv_data, int data)
|
||||
{
|
||||
struct ti_st *lhst = priv_data;
|
||||
|
||||
/* Save registration status for use in ti_st_open() */
|
||||
lhst->reg_status = data;
|
||||
/* complete the wait in ti_st_open() */
|
||||
complete(&lhst->wait_reg_completion);
|
||||
}
|
||||
|
||||
/* Called by Shared Transport layer when receive data is available */
|
||||
static long st_receive(void *priv_data, struct sk_buff *skb)
|
||||
{
|
||||
struct ti_st *lhst = priv_data;
|
||||
int err;
|
||||
|
||||
if (!skb)
|
||||
return -EFAULT;
|
||||
|
||||
if (!lhst) {
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Forward skb to HCI core layer */
|
||||
err = hci_recv_frame(lhst->hdev, skb);
|
||||
if (err < 0) {
|
||||
BT_ERR("Unable to push skb to HCI core(%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
lhst->hdev->stat.byte_rx += skb->len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------- Interfaces to HCI layer ------ */
|
||||
/* protocol structure registered with shared transport */
|
||||
static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
|
||||
{
|
||||
.chnl_id = HCI_EVENT_PKT, /* HCI Events */
|
||||
.hdr_len = sizeof(struct hci_event_hdr),
|
||||
.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
|
||||
.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
|
||||
.reserve = 8,
|
||||
},
|
||||
{
|
||||
.chnl_id = HCI_ACLDATA_PKT, /* ACL */
|
||||
.hdr_len = sizeof(struct hci_acl_hdr),
|
||||
.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
|
||||
.len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */
|
||||
.reserve = 8,
|
||||
},
|
||||
{
|
||||
.chnl_id = HCI_SCODATA_PKT, /* SCO */
|
||||
.hdr_len = sizeof(struct hci_sco_hdr),
|
||||
.offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
|
||||
.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
|
||||
.reserve = 8,
|
||||
},
|
||||
};
|
||||
|
||||
/* Called from HCI core to initialize the device */
|
||||
static int ti_st_open(struct hci_dev *hdev)
|
||||
{
|
||||
unsigned long timeleft;
|
||||
struct ti_st *hst;
|
||||
int err, i;
|
||||
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
/* provide contexts for callbacks from ST */
|
||||
hst = hci_get_drvdata(hdev);
|
||||
|
||||
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
|
||||
ti_st_proto[i].priv_data = hst;
|
||||
ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
|
||||
ti_st_proto[i].recv = st_receive;
|
||||
ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
|
||||
|
||||
/* Prepare wait-for-completion handler */
|
||||
init_completion(&hst->wait_reg_completion);
|
||||
/* Reset ST registration callback status flag,
|
||||
* this value will be updated in
|
||||
* st_reg_completion_cb()
|
||||
* function whenever it called from ST driver.
|
||||
*/
|
||||
hst->reg_status = -EINPROGRESS;
|
||||
|
||||
err = st_register(&ti_st_proto[i]);
|
||||
if (!err)
|
||||
goto done;
|
||||
|
||||
if (err != -EINPROGRESS) {
|
||||
BT_ERR("st_register failed %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ST is busy with either protocol
|
||||
* registration or firmware download.
|
||||
*/
|
||||
BT_DBG("waiting for registration "
|
||||
"completion signal from ST");
|
||||
timeleft = wait_for_completion_timeout
|
||||
(&hst->wait_reg_completion,
|
||||
msecs_to_jiffies(BT_REGISTER_TIMEOUT));
|
||||
if (!timeleft) {
|
||||
BT_ERR("Timeout(%d sec),didn't get reg "
|
||||
"completion signal from ST",
|
||||
BT_REGISTER_TIMEOUT / 1000);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Is ST registration callback
|
||||
* called with ERROR status?
|
||||
*/
|
||||
if (hst->reg_status != 0) {
|
||||
BT_ERR("ST registration completed with invalid "
|
||||
"status %d", hst->reg_status);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
done:
|
||||
hst->st_write = ti_st_proto[i].write;
|
||||
if (!hst->st_write) {
|
||||
BT_ERR("undefined ST write function");
|
||||
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
|
||||
/* Undo registration with ST */
|
||||
err = st_unregister(&ti_st_proto[i]);
|
||||
if (err)
|
||||
BT_ERR("st_unregister() failed with "
|
||||
"error %d", err);
|
||||
hst->st_write = NULL;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close device */
|
||||
static int ti_st_close(struct hci_dev *hdev)
|
||||
{
|
||||
int err, i;
|
||||
struct ti_st *hst = hci_get_drvdata(hdev);
|
||||
|
||||
for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
|
||||
err = st_unregister(&ti_st_proto[i]);
|
||||
if (err)
|
||||
BT_ERR("st_unregister(%d) failed with error %d",
|
||||
ti_st_proto[i].chnl_id, err);
|
||||
}
|
||||
|
||||
hst->st_write = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct ti_st *hst;
|
||||
long len;
|
||||
int pkt_type;
|
||||
|
||||
hst = hci_get_drvdata(hdev);
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
|
||||
|
||||
BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
|
||||
skb->len);
|
||||
|
||||
/* Insert skb to shared transport layer's transmit queue.
|
||||
* Freeing skb memory is taken care in shared transport layer,
|
||||
* so don't free skb memory here.
|
||||
*/
|
||||
pkt_type = hci_skb_pkt_type(skb);
|
||||
len = hst->st_write(skb);
|
||||
if (len < 0) {
|
||||
BT_ERR("ST write failed (%ld)", len);
|
||||
/* Try Again, would only fail if UART has gone bad */
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* ST accepted our skb. So, Go ahead and do rest */
|
||||
hdev->stat.byte_tx += len;
|
||||
ti_st_tx_complete(hst, pkt_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_ti_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_st *hst;
|
||||
struct hci_dev *hdev;
|
||||
int err;
|
||||
|
||||
hst = devm_kzalloc(&pdev->dev, sizeof(struct ti_st), GFP_KERNEL);
|
||||
if (!hst)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Expose "hciX" device to user space */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev)
|
||||
return -ENOMEM;
|
||||
|
||||
BT_DBG("hdev %p", hdev);
|
||||
|
||||
hst->hdev = hdev;
|
||||
hdev->bus = HCI_UART;
|
||||
hci_set_drvdata(hdev, hst);
|
||||
hdev->open = ti_st_open;
|
||||
hdev->close = ti_st_close;
|
||||
hdev->flush = NULL;
|
||||
hdev->send = ti_st_send_frame;
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("Can't register HCI device error %d", err);
|
||||
hci_free_dev(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_DBG("HCI device registered (hdev %p)", hdev);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, hst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_ti_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct ti_st *hst = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (!hst)
|
||||
return -EFAULT;
|
||||
|
||||
BT_DBG("%s", hst->hdev->name);
|
||||
|
||||
hdev = hst->hdev;
|
||||
ti_st_close(hdev);
|
||||
hci_unregister_dev(hdev);
|
||||
|
||||
hci_free_dev(hdev);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver btwilink_driver = {
|
||||
.probe = bt_ti_probe,
|
||||
.remove = bt_ti_remove,
|
||||
.driver = {
|
||||
.name = "btwilink",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(btwilink_driver);
|
||||
|
||||
/* ------ Module Info ------ */
|
||||
|
||||
MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -445,9 +445,11 @@ static int bcm_open(struct hci_uart *hu)
|
|||
|
||||
out:
|
||||
if (bcm->dev) {
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
hu->init_speed = bcm->dev->init_speed;
|
||||
hu->oper_speed = bcm->dev->oper_speed;
|
||||
err = bcm_gpio_set_power(bcm->dev, true);
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
if (err)
|
||||
goto err_unset_hu;
|
||||
}
|
||||
|
@ -1422,6 +1424,8 @@ static const struct of_device_id bcm_bluetooth_of_match[] = {
|
|||
{ .compatible = "brcm,bcm4345c5" },
|
||||
{ .compatible = "brcm,bcm4330-bt" },
|
||||
{ .compatible = "brcm,bcm43438-bt" },
|
||||
{ .compatible = "brcm,bcm43540-bt" },
|
||||
{ .compatible = "brcm,bcm4335a0" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match);
|
||||
|
|
|
@ -591,6 +591,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
|||
if (*ptr == 0xc0) {
|
||||
BT_ERR("Short BCSP packet");
|
||||
kfree_skb(bcsp->rx_skb);
|
||||
bcsp->rx_skb = NULL;
|
||||
bcsp->rx_state = BCSP_W4_PKT_START;
|
||||
bcsp->rx_count = 0;
|
||||
} else
|
||||
|
@ -606,6 +607,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
|||
bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
|
||||
BT_ERR("Error in BCSP hdr checksum");
|
||||
kfree_skb(bcsp->rx_skb);
|
||||
bcsp->rx_skb = NULL;
|
||||
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
|
||||
bcsp->rx_count = 0;
|
||||
continue;
|
||||
|
@ -630,6 +632,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
|||
bscp_get_crc(bcsp));
|
||||
|
||||
kfree_skb(bcsp->rx_skb);
|
||||
bcsp->rx_skb = NULL;
|
||||
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
|
||||
bcsp->rx_count = 0;
|
||||
continue;
|
||||
|
|
|
@ -621,13 +621,6 @@ static int ll_setup(struct hci_uart *hu)
|
|||
|
||||
serdev_device_set_flow_control(serdev, true);
|
||||
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
do {
|
||||
/* Reset the Bluetooth device */
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
|
||||
|
@ -639,20 +632,6 @@ static int ll_setup(struct hci_uart *hu)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (speed) {
|
||||
__le32 speed_le = cpu_to_le32(speed);
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hu->hdev,
|
||||
HCI_VS_UPDATE_UART_HCI_BAUDRATE,
|
||||
sizeof(speed_le), &speed_le,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (!IS_ERR(skb)) {
|
||||
kfree_skb(skb);
|
||||
serdev_device_set_baudrate(serdev, speed);
|
||||
}
|
||||
}
|
||||
|
||||
err = download_firmware(lldev);
|
||||
if (!err)
|
||||
break;
|
||||
|
@ -677,7 +656,25 @@ static int ll_setup(struct hci_uart *hu)
|
|||
}
|
||||
|
||||
/* Operational speed if any */
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed) {
|
||||
__le32 speed_le = cpu_to_le32(speed);
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hu->hdev, HCI_VS_UPDATE_UART_HCI_BAUDRATE,
|
||||
sizeof(speed_le), &speed_le,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (!IS_ERR(skb)) {
|
||||
kfree_skb(skb);
|
||||
serdev_device_set_baudrate(serdev, speed);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -520,7 +520,7 @@ static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|||
err = skb_pad(skb, 1);
|
||||
if (err)
|
||||
return err;
|
||||
skb_put_u8(skb, 0x00);
|
||||
skb_put(skb, 1);
|
||||
}
|
||||
|
||||
skb_queue_tail(&btdev->txq, skb);
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
#define HCI_MAX_IBS_SIZE 10
|
||||
|
||||
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
|
||||
#define IBS_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 40
|
||||
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define CMD_TRANS_TIMEOUT_MS 100
|
||||
|
||||
/* susclk rate */
|
||||
|
@ -55,6 +56,7 @@
|
|||
enum qca_flags {
|
||||
QCA_IBS_ENABLED,
|
||||
QCA_DROP_VENDOR_EVENT,
|
||||
QCA_SUSPENDING,
|
||||
};
|
||||
|
||||
/* HCI_IBS transmit side sleep protocol states */
|
||||
|
@ -100,6 +102,7 @@ struct qca_data {
|
|||
struct work_struct ws_tx_vote_off;
|
||||
unsigned long flags;
|
||||
struct completion drop_ev_comp;
|
||||
wait_queue_head_t suspend_wait_q;
|
||||
|
||||
/* For debugging purpose */
|
||||
u64 ibs_sent_wacks;
|
||||
|
@ -130,8 +133,6 @@ enum qca_speed_type {
|
|||
*/
|
||||
struct qca_vreg {
|
||||
const char *name;
|
||||
unsigned int min_uV;
|
||||
unsigned int max_uV;
|
||||
unsigned int load_uA;
|
||||
};
|
||||
|
||||
|
@ -146,8 +147,8 @@ struct qca_vreg_data {
|
|||
*/
|
||||
struct qca_power {
|
||||
struct device *dev;
|
||||
const struct qca_vreg_data *vreg_data;
|
||||
struct regulator_bulk_data *vreg_bulk;
|
||||
int num_vregs;
|
||||
bool vregs_on;
|
||||
};
|
||||
|
||||
|
@ -162,7 +163,8 @@ struct qca_serdev {
|
|||
const char *firmware_name;
|
||||
};
|
||||
|
||||
static int qca_power_setup(struct hci_uart *hu, bool on);
|
||||
static int qca_regulator_enable(struct qca_serdev *qcadev);
|
||||
static void qca_regulator_disable(struct qca_serdev *qcadev);
|
||||
static void qca_power_shutdown(struct hci_uart *hu);
|
||||
static int qca_power_off(struct hci_dev *hdev);
|
||||
|
||||
|
@ -438,6 +440,12 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
|
|||
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
|
||||
flags, SINGLE_DEPTH_NESTING);
|
||||
|
||||
/* Don't retransmit the HCI_IBS_WAKE_IND when suspending. */
|
||||
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (qca->tx_ibs_state) {
|
||||
case HCI_IBS_TX_WAKING:
|
||||
/* No WAKE_ACK, retransmit WAKE */
|
||||
|
@ -497,6 +505,8 @@ static int qca_open(struct hci_uart *hu)
|
|||
INIT_WORK(&qca->ws_rx_vote_off, qca_wq_serial_rx_clock_vote_off);
|
||||
INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off);
|
||||
|
||||
init_waitqueue_head(&qca->suspend_wait_q);
|
||||
|
||||
qca->hu = hu;
|
||||
init_completion(&qca->drop_ev_comp);
|
||||
|
||||
|
@ -518,7 +528,7 @@ static int qca_open(struct hci_uart *hu)
|
|||
} else {
|
||||
hu->init_speed = qcadev->init_speed;
|
||||
hu->oper_speed = qcadev->oper_speed;
|
||||
ret = qca_power_setup(hu, true);
|
||||
ret = qca_regulator_enable(qcadev);
|
||||
if (ret) {
|
||||
destroy_workqueue(qca->workqueue);
|
||||
kfree_skb(qca->rx_skb);
|
||||
|
@ -533,7 +543,7 @@ static int qca_open(struct hci_uart *hu)
|
|||
qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS;
|
||||
|
||||
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
||||
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
|
||||
qca->tx_idle_delay = IBS_HOST_TX_IDLE_TIMEOUT_MS;
|
||||
|
||||
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
||||
qca->tx_idle_delay, qca->wake_retrans);
|
||||
|
@ -648,6 +658,12 @@ static void device_want_to_wakeup(struct hci_uart *hu)
|
|||
|
||||
qca->ibs_recv_wakes++;
|
||||
|
||||
/* Don't wake the rx up when suspending. */
|
||||
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (qca->rx_ibs_state) {
|
||||
case HCI_IBS_RX_ASLEEP:
|
||||
/* Make sure clock is on - we may have turned clock off since
|
||||
|
@ -712,6 +728,8 @@ static void device_want_to_sleep(struct hci_uart *hu)
|
|||
break;
|
||||
}
|
||||
|
||||
wake_up_interruptible(&qca->suspend_wait_q);
|
||||
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -729,6 +747,12 @@ static void device_woke_up(struct hci_uart *hu)
|
|||
|
||||
qca->ibs_recv_wacks++;
|
||||
|
||||
/* Don't react to the wake-up-acknowledgment when suspending. */
|
||||
if (test_bit(QCA_SUSPENDING, &qca->flags)) {
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (qca->tx_ibs_state) {
|
||||
case HCI_IBS_TX_AWAKE:
|
||||
/* Expect one if we send 2 WAKEs */
|
||||
|
@ -781,8 +805,10 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
|||
|
||||
/* Don't go to sleep in middle of patch download or
|
||||
* Out-Of-Band(GPIOs control) sleep is selected.
|
||||
* Don't wake the device up when suspending.
|
||||
*/
|
||||
if (!test_bit(QCA_IBS_ENABLED, &qca->flags)) {
|
||||
if (!test_bit(QCA_IBS_ENABLED, &qca->flags) ||
|
||||
test_bit(QCA_SUSPENDING, &qca->flags)) {
|
||||
skb_queue_tail(&qca->txq, skb);
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
return 0;
|
||||
|
@ -1188,7 +1214,7 @@ static int qca_wcn3990_init(struct hci_uart *hu)
|
|||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (!qcadev->bt_power->vregs_on) {
|
||||
serdev_device_close(hu->serdev);
|
||||
ret = qca_power_setup(hu, true);
|
||||
ret = qca_regulator_enable(qcadev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1262,7 +1288,7 @@ static int qca_setup(struct hci_uart *hu)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qca_read_soc_version(hdev, &soc_ver);
|
||||
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
|
@ -1282,7 +1308,7 @@ static int qca_setup(struct hci_uart *hu)
|
|||
|
||||
if (!qca_is_wcn399x(soc_type)) {
|
||||
/* Get QCA version information */
|
||||
ret = qca_read_soc_version(hdev, &soc_ver);
|
||||
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1332,10 +1358,21 @@ static const struct hci_uart_proto qca_proto = {
|
|||
static const struct qca_vreg_data qca_soc_data_wcn3990 = {
|
||||
.soc_type = QCA_WCN3990,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
{ "vddio", 1800000, 1900000, 15000 },
|
||||
{ "vddxo", 1800000, 1900000, 80000 },
|
||||
{ "vddrf", 1300000, 1350000, 300000 },
|
||||
{ "vddch0", 3300000, 3400000, 450000 },
|
||||
{ "vddio", 15000 },
|
||||
{ "vddxo", 80000 },
|
||||
{ "vddrf", 300000 },
|
||||
{ "vddch0", 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
||||
static const struct qca_vreg_data qca_soc_data_wcn3991 = {
|
||||
.soc_type = QCA_WCN3991,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
{ "vddio", 15000 },
|
||||
{ "vddxo", 80000 },
|
||||
{ "vddrf", 300000 },
|
||||
{ "vddch0", 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
@ -1343,19 +1380,22 @@ static const struct qca_vreg_data qca_soc_data_wcn3990 = {
|
|||
static const struct qca_vreg_data qca_soc_data_wcn3998 = {
|
||||
.soc_type = QCA_WCN3998,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
{ "vddio", 1800000, 1900000, 10000 },
|
||||
{ "vddxo", 1800000, 1900000, 80000 },
|
||||
{ "vddrf", 1300000, 1352000, 300000 },
|
||||
{ "vddch0", 3300000, 3300000, 450000 },
|
||||
{ "vddio", 10000 },
|
||||
{ "vddxo", 80000 },
|
||||
{ "vddrf", 300000 },
|
||||
{ "vddch0", 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
||||
static void qca_power_shutdown(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned long flags;
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
|
||||
/* From this point we go into power off state. But serial port is
|
||||
* still open, stop queueing the IBS data and flush all the buffered
|
||||
* data in skb's.
|
||||
|
@ -1367,7 +1407,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
|
|||
|
||||
host_set_baudrate(hu, 2400);
|
||||
qca_send_power_pulse(hu, false);
|
||||
qca_power_setup(hu, false);
|
||||
qca_regulator_disable(qcadev);
|
||||
}
|
||||
|
||||
static int qca_power_off(struct hci_dev *hdev)
|
||||
|
@ -1383,97 +1423,71 @@ static int qca_power_off(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qca_enable_regulator(struct qca_vreg vregs,
|
||||
struct regulator *regulator)
|
||||
static int qca_regulator_enable(struct qca_serdev *qcadev)
|
||||
{
|
||||
struct qca_power *power = qcadev->bt_power;
|
||||
int ret;
|
||||
|
||||
ret = regulator_set_voltage(regulator, vregs.min_uV,
|
||||
vregs.max_uV);
|
||||
/* Already enabled */
|
||||
if (power->vregs_on)
|
||||
return 0;
|
||||
|
||||
BT_DBG("enabling %d regulators)", power->num_vregs);
|
||||
|
||||
ret = regulator_bulk_enable(power->num_vregs, power->vreg_bulk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vregs.load_uA)
|
||||
ret = regulator_set_load(regulator,
|
||||
vregs.load_uA);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regulator_enable(regulator);
|
||||
power->vregs_on = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qca_disable_regulator(struct qca_vreg vregs,
|
||||
struct regulator *regulator)
|
||||
static void qca_regulator_disable(struct qca_serdev *qcadev)
|
||||
{
|
||||
regulator_disable(regulator);
|
||||
regulator_set_voltage(regulator, 0, vregs.max_uV);
|
||||
if (vregs.load_uA)
|
||||
regulator_set_load(regulator, 0);
|
||||
struct qca_power *power;
|
||||
|
||||
}
|
||||
if (!qcadev)
|
||||
return;
|
||||
|
||||
static int qca_power_setup(struct hci_uart *hu, bool on)
|
||||
{
|
||||
struct qca_vreg *vregs;
|
||||
struct regulator_bulk_data *vreg_bulk;
|
||||
struct qca_serdev *qcadev;
|
||||
int i, num_vregs, ret = 0;
|
||||
power = qcadev->bt_power;
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (!qcadev || !qcadev->bt_power || !qcadev->bt_power->vreg_data ||
|
||||
!qcadev->bt_power->vreg_bulk)
|
||||
return -EINVAL;
|
||||
/* Already disabled? */
|
||||
if (!power->vregs_on)
|
||||
return;
|
||||
|
||||
vregs = qcadev->bt_power->vreg_data->vregs;
|
||||
vreg_bulk = qcadev->bt_power->vreg_bulk;
|
||||
num_vregs = qcadev->bt_power->vreg_data->num_vregs;
|
||||
BT_DBG("on: %d", on);
|
||||
if (on && !qcadev->bt_power->vregs_on) {
|
||||
for (i = 0; i < num_vregs; i++) {
|
||||
ret = qca_enable_regulator(vregs[i],
|
||||
vreg_bulk[i].consumer);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
BT_ERR("failed to enable regulator:%s", vregs[i].name);
|
||||
/* turn off regulators which are enabled */
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
qca_disable_regulator(vregs[i],
|
||||
vreg_bulk[i].consumer);
|
||||
} else {
|
||||
qcadev->bt_power->vregs_on = true;
|
||||
}
|
||||
} else if (!on && qcadev->bt_power->vregs_on) {
|
||||
/* turn off regulator in reverse order */
|
||||
i = qcadev->bt_power->vreg_data->num_vregs - 1;
|
||||
for ( ; i >= 0; i--)
|
||||
qca_disable_regulator(vregs[i], vreg_bulk[i].consumer);
|
||||
|
||||
qcadev->bt_power->vregs_on = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
regulator_bulk_disable(power->num_vregs, power->vreg_bulk);
|
||||
power->vregs_on = false;
|
||||
}
|
||||
|
||||
static int qca_init_regulators(struct qca_power *qca,
|
||||
const struct qca_vreg *vregs, size_t num_vregs)
|
||||
{
|
||||
struct regulator_bulk_data *bulk;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
qca->vreg_bulk = devm_kcalloc(qca->dev, num_vregs,
|
||||
sizeof(struct regulator_bulk_data),
|
||||
GFP_KERNEL);
|
||||
if (!qca->vreg_bulk)
|
||||
bulk = devm_kcalloc(qca->dev, num_vregs, sizeof(*bulk), GFP_KERNEL);
|
||||
if (!bulk)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_vregs; i++)
|
||||
qca->vreg_bulk[i].supply = vregs[i].name;
|
||||
bulk[i].supply = vregs[i].name;
|
||||
|
||||
return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk);
|
||||
ret = devm_regulator_bulk_get(qca->dev, num_vregs, bulk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num_vregs; i++) {
|
||||
ret = regulator_set_load(bulk[i].consumer, vregs[i].load_uA);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
qca->vreg_bulk = bulk;
|
||||
qca->num_vregs = num_vregs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
|
@ -1500,7 +1514,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
|||
return -ENOMEM;
|
||||
|
||||
qcadev->bt_power->dev = &serdev->dev;
|
||||
qcadev->bt_power->vreg_data = data;
|
||||
err = qca_init_regulators(qcadev->bt_power, data->vregs,
|
||||
data->num_vregs);
|
||||
if (err) {
|
||||
|
@ -1564,9 +1577,103 @@ static void qca_serdev_remove(struct serdev_device *serdev)
|
|||
hci_uart_unregister_device(&qcadev->serdev_hu);
|
||||
}
|
||||
|
||||
static int __maybe_unused qca_suspend(struct device *dev)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
u8 cmd;
|
||||
|
||||
set_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
/* Device is downloading patch or doesn't support in-band sleep. */
|
||||
if (!test_bit(QCA_IBS_ENABLED, &qca->flags))
|
||||
return 0;
|
||||
|
||||
cancel_work_sync(&qca->ws_awake_device);
|
||||
cancel_work_sync(&qca->ws_awake_rx);
|
||||
|
||||
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
|
||||
flags, SINGLE_DEPTH_NESTING);
|
||||
|
||||
switch (qca->tx_ibs_state) {
|
||||
case HCI_IBS_TX_WAKING:
|
||||
del_timer(&qca->wake_retrans_timer);
|
||||
/* Fall through */
|
||||
case HCI_IBS_TX_AWAKE:
|
||||
del_timer(&qca->tx_idle_timer);
|
||||
|
||||
serdev_device_write_flush(hu->serdev);
|
||||
cmd = HCI_IBS_SLEEP_IND;
|
||||
ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd));
|
||||
|
||||
if (ret < 0) {
|
||||
BT_ERR("Failed to send SLEEP to device");
|
||||
break;
|
||||
}
|
||||
|
||||
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
|
||||
qca->ibs_sent_slps++;
|
||||
|
||||
qca_wq_serial_tx_clock_vote_off(&qca->ws_tx_vote_off);
|
||||
break;
|
||||
|
||||
case HCI_IBS_TX_ASLEEP:
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Spurious tx state %d", qca->tx_ibs_state);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
serdev_device_wait_until_sent(hu->serdev,
|
||||
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
|
||||
|
||||
/* Wait for HCI_IBS_SLEEP_IND sent by device to indicate its Tx is going
|
||||
* to sleep, so that the packet does not wake the system later.
|
||||
*/
|
||||
|
||||
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
|
||||
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
|
||||
msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
|
||||
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
error:
|
||||
clear_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused qca_resume(struct device *dev)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(dev, struct hci_dev, dev);
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
clear_bit(QCA_SUSPENDING, &qca->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
|
||||
|
||||
static const struct of_device_id qca_bluetooth_of_match[] = {
|
||||
{ .compatible = "qcom,qca6174-bt" },
|
||||
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
|
||||
{ .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
|
||||
{ .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
@ -1578,6 +1685,7 @@ static struct serdev_device_driver qca_serdev_driver = {
|
|||
.driver = {
|
||||
.name = "hci_uart_qca",
|
||||
.of_match_table = qca_bluetooth_of_match,
|
||||
.pm = &qca_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -104,10 +104,8 @@ static int __fsl_mc_device_match(struct device *dev, void *data)
|
|||
return fsl_mc_device_match(mc_dev, obj_desc);
|
||||
}
|
||||
|
||||
static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc
|
||||
*obj_desc,
|
||||
struct fsl_mc_device
|
||||
*mc_bus_dev)
|
||||
struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
|
||||
struct fsl_mc_device *mc_bus_dev)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
|
|
|
@ -554,3 +554,56 @@ int dprc_get_container_id(struct fsl_mc_io *mc_io,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dprc_get_connection() - Get connected endpoint and link status if connection
|
||||
* exists.
|
||||
* @mc_io: Pointer to MC portal's I/O object
|
||||
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
|
||||
* @token: Token of DPRC object
|
||||
* @endpoint1: Endpoint 1 configuration parameters
|
||||
* @endpoint2: Returned endpoint 2 configuration parameters
|
||||
* @state: Returned link state:
|
||||
* 1 - link is up;
|
||||
* 0 - link is down;
|
||||
* -1 - no connection (endpoint2 information is irrelevant)
|
||||
*
|
||||
* Return: '0' on Success; -ENOTCONN if connection does not exist.
|
||||
*/
|
||||
int dprc_get_connection(struct fsl_mc_io *mc_io,
|
||||
u32 cmd_flags,
|
||||
u16 token,
|
||||
const struct dprc_endpoint *endpoint1,
|
||||
struct dprc_endpoint *endpoint2,
|
||||
int *state)
|
||||
{
|
||||
struct dprc_cmd_get_connection *cmd_params;
|
||||
struct dprc_rsp_get_connection *rsp_params;
|
||||
struct fsl_mc_command cmd = { 0 };
|
||||
int err, i;
|
||||
|
||||
/* prepare command */
|
||||
cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONNECTION,
|
||||
cmd_flags,
|
||||
token);
|
||||
cmd_params = (struct dprc_cmd_get_connection *)cmd.params;
|
||||
cmd_params->ep1_id = cpu_to_le32(endpoint1->id);
|
||||
cmd_params->ep1_interface_id = cpu_to_le16(endpoint1->if_id);
|
||||
for (i = 0; i < 16; i++)
|
||||
cmd_params->ep1_type[i] = endpoint1->type[i];
|
||||
|
||||
/* send command to mc */
|
||||
err = mc_send_command(mc_io, &cmd);
|
||||
if (err)
|
||||
return -ENOTCONN;
|
||||
|
||||
/* retrieve response parameters */
|
||||
rsp_params = (struct dprc_rsp_get_connection *)cmd.params;
|
||||
endpoint2->id = le32_to_cpu(rsp_params->ep2_id);
|
||||
endpoint2->if_id = le16_to_cpu(rsp_params->ep2_interface_id);
|
||||
*state = le32_to_cpu(rsp_params->state);
|
||||
for (i = 0; i < 16; i++)
|
||||
endpoint2->type[i] = rsp_params->ep2_type[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -166,42 +166,52 @@ EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
|
|||
struct device_type fsl_mc_bus_dprc_type = {
|
||||
.name = "fsl_mc_bus_dprc"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpni_type = {
|
||||
.name = "fsl_mc_bus_dpni"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpio_type = {
|
||||
.name = "fsl_mc_bus_dpio"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpsw_type = {
|
||||
.name = "fsl_mc_bus_dpsw"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpbp_type = {
|
||||
.name = "fsl_mc_bus_dpbp"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpcon_type = {
|
||||
.name = "fsl_mc_bus_dpcon"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpmcp_type = {
|
||||
.name = "fsl_mc_bus_dpmcp"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpmac_type = {
|
||||
.name = "fsl_mc_bus_dpmac"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dprtc_type = {
|
||||
.name = "fsl_mc_bus_dprtc"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type);
|
||||
|
||||
struct device_type fsl_mc_bus_dpseci_type = {
|
||||
.name = "fsl_mc_bus_dpseci"
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type);
|
||||
|
||||
static struct device_type *fsl_mc_get_device_type(const char *type)
|
||||
{
|
||||
|
@ -702,6 +712,39 @@ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
|
||||
|
||||
struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev)
|
||||
{
|
||||
struct fsl_mc_device *mc_bus_dev, *endpoint;
|
||||
struct fsl_mc_obj_desc endpoint_desc = { 0 };
|
||||
struct dprc_endpoint endpoint1 = { 0 };
|
||||
struct dprc_endpoint endpoint2 = { 0 };
|
||||
int state, err;
|
||||
|
||||
mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
|
||||
strcpy(endpoint1.type, mc_dev->obj_desc.type);
|
||||
endpoint1.id = mc_dev->obj_desc.id;
|
||||
|
||||
err = dprc_get_connection(mc_bus_dev->mc_io, 0,
|
||||
mc_bus_dev->mc_handle,
|
||||
&endpoint1, &endpoint2,
|
||||
&state);
|
||||
|
||||
if (err == -ENOTCONN || state == -1)
|
||||
return ERR_PTR(-ENOTCONN);
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&mc_bus_dev->dev, "dprc_get_connection() = %d\n", err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
strcpy(endpoint_desc.type, endpoint2.type);
|
||||
endpoint_desc.id = endpoint2.id;
|
||||
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint);
|
||||
|
||||
static int parse_mc_ranges(struct device *dev,
|
||||
int *paddr_cells,
|
||||
int *mc_addr_cells,
|
||||
|
|
|
@ -105,6 +105,8 @@ int dpmcp_reset(struct fsl_mc_io *mc_io,
|
|||
#define DPRC_CMDID_GET_OBJ_REG_V2 DPRC_CMD_V2(0x15E)
|
||||
#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F)
|
||||
|
||||
#define DPRC_CMDID_GET_CONNECTION DPRC_CMD(0x16C)
|
||||
|
||||
struct dprc_cmd_open {
|
||||
__le32 container_id;
|
||||
};
|
||||
|
@ -228,6 +230,22 @@ struct dprc_cmd_set_obj_irq {
|
|||
u8 obj_type[16];
|
||||
};
|
||||
|
||||
struct dprc_cmd_get_connection {
|
||||
__le32 ep1_id;
|
||||
__le16 ep1_interface_id;
|
||||
u8 pad[2];
|
||||
u8 ep1_type[16];
|
||||
};
|
||||
|
||||
struct dprc_rsp_get_connection {
|
||||
__le64 pad[3];
|
||||
__le32 ep2_id;
|
||||
__le16 ep2_interface_id;
|
||||
__le16 pad1;
|
||||
u8 ep2_type[16];
|
||||
__le32 state;
|
||||
};
|
||||
|
||||
/*
|
||||
* DPRC API for managing and querying DPAA resources
|
||||
*/
|
||||
|
@ -392,6 +410,27 @@ int dprc_get_container_id(struct fsl_mc_io *mc_io,
|
|||
u32 cmd_flags,
|
||||
int *container_id);
|
||||
|
||||
/**
|
||||
* struct dprc_endpoint - Endpoint description for link connect/disconnect
|
||||
* operations
|
||||
* @type: Endpoint object type: NULL terminated string
|
||||
* @id: Endpoint object ID
|
||||
* @if_id: Interface ID; should be set for endpoints with multiple
|
||||
* interfaces ("dpsw", "dpdmux"); for others, always set to 0
|
||||
*/
|
||||
struct dprc_endpoint {
|
||||
char type[16];
|
||||
int id;
|
||||
u16 if_id;
|
||||
};
|
||||
|
||||
int dprc_get_connection(struct fsl_mc_io *mc_io,
|
||||
u32 cmd_flags,
|
||||
u16 token,
|
||||
const struct dprc_endpoint *endpoint1,
|
||||
struct dprc_endpoint *endpoint2,
|
||||
int *state);
|
||||
|
||||
/*
|
||||
* Data Path Buffer Pool (DPBP) API
|
||||
*/
|
||||
|
@ -574,4 +613,7 @@ void fsl_destroy_mc_io(struct fsl_mc_io *mc_io);
|
|||
|
||||
bool fsl_mc_is_root_dprc(struct device *dev);
|
||||
|
||||
struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,
|
||||
struct fsl_mc_device *mc_bus_dev);
|
||||
|
||||
#endif /* _FSL_MC_PRIVATE_H_ */
|
||||
|
|
|
@ -35,7 +35,7 @@ config CHELSIO_IPSEC_INLINE
|
|||
config CRYPTO_DEV_CHELSIO_TLS
|
||||
tristate "Chelsio Crypto Inline TLS Driver"
|
||||
depends on CHELSIO_T4
|
||||
depends on TLS
|
||||
depends on TLS_TOE
|
||||
select CRYPTO_DEV_CHELSIO
|
||||
---help---
|
||||
Support Chelsio Inline TLS with Chelsio crypto accelerator.
|
||||
|
|
|
@ -673,16 +673,16 @@ static inline void txq_advance(struct sge_txq *q, unsigned int n)
|
|||
int chcr_ipsec_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct xfrm_state *x = xfrm_input_state(skb);
|
||||
unsigned int last_desc, ndesc, flits = 0;
|
||||
struct ipsec_sa_entry *sa_entry;
|
||||
u64 *pos, *end, *before, *sgl;
|
||||
struct tx_sw_desc *sgl_sdesc;
|
||||
int qidx, left, credits;
|
||||
unsigned int flits = 0, ndesc;
|
||||
struct adapter *adap;
|
||||
struct sge_eth_txq *q;
|
||||
struct port_info *pi;
|
||||
dma_addr_t addr[MAX_SKB_FRAGS + 1];
|
||||
struct sec_path *sp;
|
||||
bool immediate = false;
|
||||
struct sge_eth_txq *q;
|
||||
struct adapter *adap;
|
||||
struct port_info *pi;
|
||||
struct sec_path *sp;
|
||||
|
||||
if (!x->xso.offload_handle)
|
||||
return NETDEV_TX_BUSY;
|
||||
|
@ -715,8 +715,14 @@ out_free: dev_kfree_skb_any(skb);
|
|||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
last_desc = q->q.pidx + ndesc - 1;
|
||||
if (last_desc >= q->q.size)
|
||||
last_desc -= q->q.size;
|
||||
sgl_sdesc = &q->q.sdesc[last_desc];
|
||||
|
||||
if (!immediate &&
|
||||
unlikely(cxgb4_map_skb(adap->pdev_dev, skb, addr) < 0)) {
|
||||
unlikely(cxgb4_map_skb(adap->pdev_dev, skb, sgl_sdesc->addr) < 0)) {
|
||||
memset(sgl_sdesc->addr, 0, sizeof(sgl_sdesc->addr));
|
||||
q->mapping_err++;
|
||||
goto out_free;
|
||||
}
|
||||
|
@ -742,17 +748,10 @@ out_free: dev_kfree_skb_any(skb);
|
|||
cxgb4_inline_tx_skb(skb, &q->q, sgl);
|
||||
dev_consume_skb_any(skb);
|
||||
} else {
|
||||
int last_desc;
|
||||
|
||||
cxgb4_write_sgl(skb, &q->q, (void *)sgl, end,
|
||||
0, addr);
|
||||
0, sgl_sdesc->addr);
|
||||
skb_orphan(skb);
|
||||
|
||||
last_desc = q->q.pidx + ndesc - 1;
|
||||
if (last_desc >= q->q.size)
|
||||
last_desc -= q->q.size;
|
||||
q->q.sdesc[last_desc].skb = skb;
|
||||
q->q.sdesc[last_desc].sgl = (struct ulptx_sgl *)sgl;
|
||||
sgl_sdesc->skb = skb;
|
||||
}
|
||||
txq_advance(&q->q, ndesc);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <crypto/internal/hash.h>
|
||||
#include <linux/tls.h>
|
||||
#include <net/tls.h>
|
||||
#include <net/tls_toe.h>
|
||||
|
||||
#include "t4fw_api.h"
|
||||
#include "t4_msg.h"
|
||||
|
@ -118,7 +119,7 @@ struct tls_scmd {
|
|||
};
|
||||
|
||||
struct chtls_dev {
|
||||
struct tls_device tlsdev;
|
||||
struct tls_toe_device tlsdev;
|
||||
struct list_head list;
|
||||
struct cxgb4_lld_info *lldi;
|
||||
struct pci_dev *pdev;
|
||||
|
@ -362,7 +363,7 @@ enum {
|
|||
#define TCP_PAGE(sk) (sk->sk_frag.page)
|
||||
#define TCP_OFF(sk) (sk->sk_frag.offset)
|
||||
|
||||
static inline struct chtls_dev *to_chtls_dev(struct tls_device *tlsdev)
|
||||
static inline struct chtls_dev *to_chtls_dev(struct tls_toe_device *tlsdev)
|
||||
{
|
||||
return container_of(tlsdev, struct chtls_dev, tlsdev);
|
||||
}
|
||||
|
|
|
@ -1437,7 +1437,7 @@ static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||
csk->wr_max_credits))
|
||||
sk->sk_write_space(sk);
|
||||
|
||||
if (copied >= target && !sk->sk_backlog.tail)
|
||||
if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
|
||||
break;
|
||||
|
||||
if (copied) {
|
||||
|
@ -1470,7 +1470,7 @@ static int chtls_pt_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (sk->sk_backlog.tail) {
|
||||
if (READ_ONCE(sk->sk_backlog.tail)) {
|
||||
release_sock(sk);
|
||||
lock_sock(sk);
|
||||
chtls_cleanup_rbuf(sk, copied);
|
||||
|
@ -1615,7 +1615,7 @@ static int peekmsg(struct sock *sk, struct msghdr *msg,
|
|||
break;
|
||||
}
|
||||
|
||||
if (sk->sk_backlog.tail) {
|
||||
if (READ_ONCE(sk->sk_backlog.tail)) {
|
||||
/* Do not sleep, just process backlog. */
|
||||
release_sock(sk);
|
||||
lock_sock(sk);
|
||||
|
@ -1743,7 +1743,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||
csk->wr_max_credits))
|
||||
sk->sk_write_space(sk);
|
||||
|
||||
if (copied >= target && !sk->sk_backlog.tail)
|
||||
if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
|
||||
break;
|
||||
|
||||
if (copied) {
|
||||
|
@ -1774,7 +1774,7 @@ int chtls_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||
}
|
||||
}
|
||||
|
||||
if (sk->sk_backlog.tail) {
|
||||
if (READ_ONCE(sk->sk_backlog.tail)) {
|
||||
release_sock(sk);
|
||||
lock_sock(sk);
|
||||
chtls_cleanup_rbuf(sk, copied);
|
||||
|
|
|
@ -124,7 +124,7 @@ static void chtls_stop_listen(struct chtls_dev *cdev, struct sock *sk)
|
|||
mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
static int chtls_inline_feature(struct tls_device *dev)
|
||||
static int chtls_inline_feature(struct tls_toe_device *dev)
|
||||
{
|
||||
struct net_device *netdev;
|
||||
struct chtls_dev *cdev;
|
||||
|
@ -140,7 +140,7 @@ static int chtls_inline_feature(struct tls_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
|
||||
static int chtls_create_hash(struct tls_toe_device *dev, struct sock *sk)
|
||||
{
|
||||
struct chtls_dev *cdev = to_chtls_dev(dev);
|
||||
|
||||
|
@ -149,7 +149,7 @@ static int chtls_create_hash(struct tls_device *dev, struct sock *sk)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk)
|
||||
static void chtls_destroy_hash(struct tls_toe_device *dev, struct sock *sk)
|
||||
{
|
||||
struct chtls_dev *cdev = to_chtls_dev(dev);
|
||||
|
||||
|
@ -161,7 +161,7 @@ static void chtls_free_uld(struct chtls_dev *cdev)
|
|||
{
|
||||
int i;
|
||||
|
||||
tls_unregister_device(&cdev->tlsdev);
|
||||
tls_toe_unregister_device(&cdev->tlsdev);
|
||||
kvfree(cdev->kmap.addr);
|
||||
idr_destroy(&cdev->hwtid_idr);
|
||||
for (i = 0; i < (1 << RSPQ_HASH_BITS); i++)
|
||||
|
@ -173,27 +173,27 @@ static void chtls_free_uld(struct chtls_dev *cdev)
|
|||
|
||||
static inline void chtls_dev_release(struct kref *kref)
|
||||
{
|
||||
struct tls_toe_device *dev;
|
||||
struct chtls_dev *cdev;
|
||||
struct tls_device *dev;
|
||||
|
||||
dev = container_of(kref, struct tls_device, kref);
|
||||
dev = container_of(kref, struct tls_toe_device, kref);
|
||||
cdev = to_chtls_dev(dev);
|
||||
chtls_free_uld(cdev);
|
||||
}
|
||||
|
||||
static void chtls_register_dev(struct chtls_dev *cdev)
|
||||
{
|
||||
struct tls_device *tlsdev = &cdev->tlsdev;
|
||||
struct tls_toe_device *tlsdev = &cdev->tlsdev;
|
||||
|
||||
strlcpy(tlsdev->name, "chtls", TLS_DEVICE_NAME_MAX);
|
||||
strlcpy(tlsdev->name, "chtls", TLS_TOE_DEVICE_NAME_MAX);
|
||||
strlcat(tlsdev->name, cdev->lldi->ports[0]->name,
|
||||
TLS_DEVICE_NAME_MAX);
|
||||
TLS_TOE_DEVICE_NAME_MAX);
|
||||
tlsdev->feature = chtls_inline_feature;
|
||||
tlsdev->hash = chtls_create_hash;
|
||||
tlsdev->unhash = chtls_destroy_hash;
|
||||
tlsdev->release = chtls_dev_release;
|
||||
kref_init(&tlsdev->kref);
|
||||
tls_register_device(tlsdev);
|
||||
tls_toe_register_device(tlsdev);
|
||||
cdev->cdev_state = CHTLS_CDEV_STATE_UP;
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,11 @@ static int fwnet_header_cache(const struct neighbour *neigh,
|
|||
h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
|
||||
h->h_proto = type;
|
||||
memcpy(h->h_dest, neigh->ha, net->addr_len);
|
||||
hh->hh_len = FWNET_HLEN;
|
||||
|
||||
/* Pairs with the READ_ONCE() in neigh_resolve_output(),
|
||||
* neigh_hh_output() and neigh_update_hhs().
|
||||
*/
|
||||
smp_store_release(&hh->hh_len, FWNET_HLEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -22,3 +22,11 @@ config BCM47XX_SPROM
|
|||
In case of SoC devices SPROM content is stored on a flash used by
|
||||
bootloader firmware CFE. This driver provides method to ssb and bcma
|
||||
drivers to read SPROM on SoC.
|
||||
|
||||
config TEE_BNXT_FW
|
||||
tristate "Broadcom BNXT firmware manager"
|
||||
depends on (ARCH_BCM_IPROC && OPTEE) || (COMPILE_TEST && TEE)
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
This module help to manage firmware on Broadcom BNXT device. The module
|
||||
registers on tee bus and invoke calls to manage firmware on BNXT device.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o
|
||||
obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o
|
||||
obj-$(CONFIG_TEE_BNXT_FW) += tee_bnxt_fw.o
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 Broadcom.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tee_drv.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include <linux/firmware/broadcom/tee_bnxt_fw.h>
|
||||
|
||||
#define MAX_SHM_MEM_SZ SZ_4M
|
||||
|
||||
#define MAX_TEE_PARAM_ARRY_MEMB 4
|
||||
|
||||
enum ta_cmd {
|
||||
/*
|
||||
* TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram
|
||||
*
|
||||
* param[0] unused
|
||||
* param[1] unused
|
||||
* param[2] unused
|
||||
* param[3] unused
|
||||
*
|
||||
* Result:
|
||||
* TEE_SUCCESS - Invoke command success
|
||||
* TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory
|
||||
*/
|
||||
TA_CMD_BNXT_FASTBOOT = 0,
|
||||
|
||||
/*
|
||||
* TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm
|
||||
*
|
||||
* param[0] (inout memref) - Coredump buffer memory reference
|
||||
* param[1] (in value) - value.a: offset, data to be copied from
|
||||
* value.b: size of data to be copied
|
||||
* param[2] unused
|
||||
* param[3] unused
|
||||
*
|
||||
* Result:
|
||||
* TEE_SUCCESS - Invoke command success
|
||||
* TEE_ERROR_BAD_PARAMETERS - Incorrect input param
|
||||
* TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump
|
||||
*/
|
||||
TA_CMD_BNXT_COPY_COREDUMP = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tee_bnxt_fw_private - OP-TEE bnxt private data
|
||||
* @dev: OP-TEE based bnxt device.
|
||||
* @ctx: OP-TEE context handler.
|
||||
* @session_id: TA session identifier.
|
||||
*/
|
||||
struct tee_bnxt_fw_private {
|
||||
struct device *dev;
|
||||
struct tee_context *ctx;
|
||||
u32 session_id;
|
||||
struct tee_shm *fw_shm_pool;
|
||||
};
|
||||
|
||||
static struct tee_bnxt_fw_private pvt_data;
|
||||
|
||||
static void prepare_args(int cmd,
|
||||
struct tee_ioctl_invoke_arg *arg,
|
||||
struct tee_param *param)
|
||||
{
|
||||
memset(arg, 0, sizeof(*arg));
|
||||
memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param));
|
||||
|
||||
arg->func = cmd;
|
||||
arg->session = pvt_data.session_id;
|
||||
arg->num_params = MAX_TEE_PARAM_ARRY_MEMB;
|
||||
|
||||
/* Fill invoke cmd params */
|
||||
switch (cmd) {
|
||||
case TA_CMD_BNXT_COPY_COREDUMP:
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
|
||||
param[0].u.memref.shm = pvt_data.fw_shm_pool;
|
||||
param[0].u.memref.size = MAX_SHM_MEM_SZ;
|
||||
param[0].u.memref.shm_offs = 0;
|
||||
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
|
||||
break;
|
||||
case TA_CMD_BNXT_FASTBOOT:
|
||||
default:
|
||||
/* Nothing to do */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_bnxt_fw_load() - Load the bnxt firmware
|
||||
* Uses an OP-TEE call to start a secure
|
||||
* boot process.
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int tee_bnxt_fw_load(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tee_ioctl_invoke_arg arg;
|
||||
struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];
|
||||
|
||||
if (!pvt_data.ctx)
|
||||
return -ENODEV;
|
||||
|
||||
prepare_args(TA_CMD_BNXT_FASTBOOT, &arg, param);
|
||||
|
||||
ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
|
||||
if (ret < 0 || arg.ret != 0) {
|
||||
dev_err(pvt_data.dev,
|
||||
"TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n",
|
||||
arg.ret, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tee_bnxt_fw_load);
|
||||
|
||||
/**
|
||||
* tee_bnxt_copy_coredump() - Copy coredump from the allocated memory
|
||||
* Uses an OP-TEE call to copy coredump
|
||||
* @buf: destination buffer where core dump is copied into
|
||||
* @offset: offset from the base address of core dump area
|
||||
* @size: size of the dump
|
||||
*
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size)
|
||||
{
|
||||
struct tee_ioctl_invoke_arg arg;
|
||||
struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB];
|
||||
void *core_data;
|
||||
u32 rbytes = size;
|
||||
u32 nbytes = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!pvt_data.ctx)
|
||||
return -ENODEV;
|
||||
|
||||
prepare_args(TA_CMD_BNXT_COPY_COREDUMP, &arg, param);
|
||||
|
||||
while (rbytes) {
|
||||
nbytes = rbytes;
|
||||
|
||||
nbytes = min_t(u32, rbytes, param[0].u.memref.size);
|
||||
|
||||
/* Fill additional invoke cmd params */
|
||||
param[1].u.value.a = offset;
|
||||
param[1].u.value.b = nbytes;
|
||||
|
||||
ret = tee_client_invoke_func(pvt_data.ctx, &arg, param);
|
||||
if (ret < 0 || arg.ret != 0) {
|
||||
dev_err(pvt_data.dev,
|
||||
"TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n",
|
||||
arg.ret, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
core_data = tee_shm_get_va(pvt_data.fw_shm_pool, 0);
|
||||
if (IS_ERR(core_data)) {
|
||||
dev_err(pvt_data.dev, "tee_shm_get_va failed\n");
|
||||
return PTR_ERR(core_data);
|
||||
}
|
||||
|
||||
memcpy(buf, core_data, nbytes);
|
||||
|
||||
rbytes -= nbytes;
|
||||
buf += nbytes;
|
||||
offset += nbytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tee_bnxt_copy_coredump);
|
||||
|
||||
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
|
||||
{
|
||||
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
|
||||
}
|
||||
|
||||
static int tee_bnxt_fw_probe(struct device *dev)
|
||||
{
|
||||
struct tee_client_device *bnxt_device = to_tee_client_device(dev);
|
||||
int ret, err = -ENODEV;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
struct tee_shm *fw_shm_pool;
|
||||
|
||||
memset(&sess_arg, 0, sizeof(sess_arg));
|
||||
|
||||
/* Open context with TEE driver */
|
||||
pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
|
||||
NULL);
|
||||
if (IS_ERR(pvt_data.ctx))
|
||||
return -ENODEV;
|
||||
|
||||
/* Open session with Bnxt load Trusted App */
|
||||
memcpy(sess_arg.uuid, bnxt_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
|
||||
sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
|
||||
sess_arg.num_params = 0;
|
||||
|
||||
ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
|
||||
if (ret < 0 || sess_arg.ret != 0) {
|
||||
dev_err(dev, "tee_client_open_session failed, err: %x\n",
|
||||
sess_arg.ret);
|
||||
err = -EINVAL;
|
||||
goto out_ctx;
|
||||
}
|
||||
pvt_data.session_id = sess_arg.session;
|
||||
|
||||
pvt_data.dev = dev;
|
||||
|
||||
fw_shm_pool = tee_shm_alloc(pvt_data.ctx, MAX_SHM_MEM_SZ,
|
||||
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
|
||||
if (IS_ERR(fw_shm_pool)) {
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
dev_err(pvt_data.dev, "tee_shm_alloc failed\n");
|
||||
err = PTR_ERR(fw_shm_pool);
|
||||
goto out_sess;
|
||||
}
|
||||
|
||||
pvt_data.fw_shm_pool = fw_shm_pool;
|
||||
|
||||
return 0;
|
||||
|
||||
out_sess:
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
out_ctx:
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tee_bnxt_fw_remove(struct device *dev)
|
||||
{
|
||||
tee_shm_free(pvt_data.fw_shm_pool);
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
pvt_data.ctx = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
|
||||
{UUID_INIT(0x6272636D, 0x2019, 0x0716,
|
||||
0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table);
|
||||
|
||||
static struct tee_client_driver tee_bnxt_fw_driver = {
|
||||
.id_table = tee_bnxt_fw_id_table,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.bus = &tee_bus_type,
|
||||
.probe = tee_bnxt_fw_probe,
|
||||
.remove = tee_bnxt_fw_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init tee_bnxt_fw_mod_init(void)
|
||||
{
|
||||
return driver_register(&tee_bnxt_fw_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit tee_bnxt_fw_mod_exit(void)
|
||||
{
|
||||
driver_unregister(&tee_bnxt_fw_driver.driver);
|
||||
}
|
||||
|
||||
module_init(tee_bnxt_fw_mod_init);
|
||||
module_exit(tee_bnxt_fw_mod_exit);
|
||||
|
||||
MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>");
|
||||
MODULE_DESCRIPTION("Broadcom bnxt firmware manager");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -881,8 +881,8 @@ struct sdma_engine *sdma_select_user_engine(struct hfi1_devdata *dd,
|
|||
|
||||
cpu_id = smp_processor_id();
|
||||
rcu_read_lock();
|
||||
rht_node = rhashtable_lookup_fast(dd->sdma_rht, &cpu_id,
|
||||
sdma_rht_params);
|
||||
rht_node = rhashtable_lookup(dd->sdma_rht, &cpu_id,
|
||||
sdma_rht_params);
|
||||
|
||||
if (rht_node && rht_node->map[vl]) {
|
||||
struct sdma_rht_map_elem *map = rht_node->map[vl];
|
||||
|
|
|
@ -35,7 +35,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
|
|||
int vport_index;
|
||||
|
||||
if (rep->vport == MLX5_VPORT_UPLINK)
|
||||
profile = &uplink_rep_profile;
|
||||
profile = &raw_eth_profile;
|
||||
else
|
||||
return mlx5_ib_set_vport_rep(dev, rep);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "mlx5_ib.h"
|
||||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
extern const struct mlx5_ib_profile uplink_rep_profile;
|
||||
extern const struct mlx5_ib_profile raw_eth_profile;
|
||||
|
||||
u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw);
|
||||
struct mlx5_ib_dev *mlx5_ib_get_rep_ibdev(struct mlx5_eswitch *esw,
|
||||
|
|
|
@ -1031,7 +1031,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
|
|||
if (MLX5_CAP_GEN(mdev, cd))
|
||||
props->device_cap_flags |= IB_DEVICE_CROSS_CHANNEL;
|
||||
|
||||
if (!mlx5_core_is_pf(mdev))
|
||||
if (mlx5_core_is_vf(mdev))
|
||||
props->device_cap_flags |= IB_DEVICE_VIRTUAL_FUNCTION;
|
||||
|
||||
if (mlx5_ib_port_link_layer(ibdev, 1) ==
|
||||
|
@ -5145,8 +5145,7 @@ static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
|
|||
immutable->pkey_tbl_len = attr.pkey_tbl_len;
|
||||
immutable->gid_tbl_len = attr.gid_tbl_len;
|
||||
immutable->core_cap_flags = get_core_cap_flags(ibdev, &rep);
|
||||
if ((ll == IB_LINK_LAYER_INFINIBAND) || MLX5_CAP_GEN(dev->mdev, roce))
|
||||
immutable->max_mad_size = IB_MGMT_MAD_SIZE;
|
||||
immutable->max_mad_size = IB_MGMT_MAD_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5249,11 +5248,9 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev)
|
|||
{
|
||||
int err;
|
||||
|
||||
if (MLX5_CAP_GEN(dev->mdev, roce)) {
|
||||
err = mlx5_nic_vport_enable_roce(dev->mdev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = mlx5_nic_vport_enable_roce(dev->mdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mlx5_eth_lag_init(dev);
|
||||
if (err)
|
||||
|
@ -5262,8 +5259,7 @@ static int mlx5_enable_eth(struct mlx5_ib_dev *dev)
|
|||
return 0;
|
||||
|
||||
err_disable_roce:
|
||||
if (MLX5_CAP_GEN(dev->mdev, roce))
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -5271,8 +5267,7 @@ err_disable_roce:
|
|||
static void mlx5_disable_eth(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_eth_lag_cleanup(dev);
|
||||
if (MLX5_CAP_GEN(dev->mdev, roce))
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
mlx5_nic_vport_disable_roce(dev->mdev);
|
||||
}
|
||||
|
||||
struct mlx5_ib_counter {
|
||||
|
@ -6444,7 +6439,7 @@ static const struct ib_device_ops mlx5_ib_dev_port_rep_ops = {
|
|||
.query_port = mlx5_ib_rep_query_port,
|
||||
};
|
||||
|
||||
static int mlx5_ib_stage_rep_non_default_cb(struct mlx5_ib_dev *dev)
|
||||
static int mlx5_ib_stage_raw_eth_non_default_cb(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
ib_set_device_ops(&dev->ib_dev, &mlx5_ib_dev_port_rep_ops);
|
||||
return 0;
|
||||
|
@ -6484,7 +6479,7 @@ static void mlx5_ib_stage_common_roce_cleanup(struct mlx5_ib_dev *dev)
|
|||
mlx5_remove_netdev_notifier(dev, port_num);
|
||||
}
|
||||
|
||||
static int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
|
||||
static int mlx5_ib_stage_raw_eth_roce_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = dev->mdev;
|
||||
enum rdma_link_layer ll;
|
||||
|
@ -6500,7 +6495,7 @@ static int mlx5_ib_stage_rep_roce_init(struct mlx5_ib_dev *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_ib_stage_rep_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
static void mlx5_ib_stage_raw_eth_roce_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
mlx5_ib_stage_common_roce_cleanup(dev);
|
||||
}
|
||||
|
@ -6807,7 +6802,7 @@ static const struct mlx5_ib_profile pf_profile = {
|
|||
mlx5_ib_stage_delay_drop_cleanup),
|
||||
};
|
||||
|
||||
const struct mlx5_ib_profile uplink_rep_profile = {
|
||||
const struct mlx5_ib_profile raw_eth_profile = {
|
||||
STAGE_CREATE(MLX5_IB_STAGE_INIT,
|
||||
mlx5_ib_stage_init_init,
|
||||
mlx5_ib_stage_init_cleanup),
|
||||
|
@ -6818,11 +6813,11 @@ const struct mlx5_ib_profile uplink_rep_profile = {
|
|||
mlx5_ib_stage_caps_init,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_NON_DEFAULT_CB,
|
||||
mlx5_ib_stage_rep_non_default_cb,
|
||||
mlx5_ib_stage_raw_eth_non_default_cb,
|
||||
NULL),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_ROCE,
|
||||
mlx5_ib_stage_rep_roce_init,
|
||||
mlx5_ib_stage_rep_roce_cleanup),
|
||||
mlx5_ib_stage_raw_eth_roce_init,
|
||||
mlx5_ib_stage_raw_eth_roce_cleanup),
|
||||
STAGE_CREATE(MLX5_IB_STAGE_SRQ,
|
||||
mlx5_init_srq_table,
|
||||
mlx5_cleanup_srq_table),
|
||||
|
@ -6898,6 +6893,7 @@ static void *mlx5_ib_add_slave_port(struct mlx5_core_dev *mdev)
|
|||
|
||||
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
||||
{
|
||||
const struct mlx5_ib_profile *profile;
|
||||
enum rdma_link_layer ll;
|
||||
struct mlx5_ib_dev *dev;
|
||||
int port_type_cap;
|
||||
|
@ -6933,7 +6929,12 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
|||
dev->mdev = mdev;
|
||||
dev->num_ports = num_ports;
|
||||
|
||||
return __mlx5_ib_add(dev, &pf_profile);
|
||||
if (ll == IB_LINK_LAYER_ETHERNET && !mlx5_is_roce_enabled(mdev))
|
||||
profile = &raw_eth_profile;
|
||||
else
|
||||
profile = &pf_profile;
|
||||
|
||||
return __mlx5_ib_add(dev, profile);
|
||||
}
|
||||
|
||||
static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context)
|
||||
|
|
|
@ -10,7 +10,7 @@ config MISDN_HFCPCI
|
|||
depends on PCI
|
||||
help
|
||||
Enable support for cards with Cologne Chip AG's
|
||||
HFC PCI chip.
|
||||
HFC PCI chip.
|
||||
|
||||
config MISDN_HFCMULTI
|
||||
tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
|
||||
|
|
|
@ -173,8 +173,8 @@ symbolic(struct hfcusb_symbolic_list list[], const int num)
|
|||
|
||||
|
||||
/*
|
||||
* List of all supported enpoints configiration sets, used to find the
|
||||
* best matching endpoint configuration within a devices' USB descriptor.
|
||||
* List of all supported endpoint configuration sets, used to find the
|
||||
* best matching endpoint configuration within a device's USB descriptor.
|
||||
* We need at least 3 RX endpoints, and 3 TX endpoints, either
|
||||
* INT-in and ISO-out, or ISO-in and ISO-out)
|
||||
* with 4 RX endpoints even E-Channel logging is possible
|
||||
|
|
|
@ -27,7 +27,6 @@ MODULE_VERSION(ISAR_REV);
|
|||
|
||||
#define DEBUG_HW_FIRMWARE_FIFO 0x10000
|
||||
|
||||
static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
|
||||
static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121,
|
||||
122, 145, 146};
|
||||
#define FAXMODCNT 13
|
||||
|
|
|
@ -28,6 +28,10 @@ MODULE_PARM_DESC(disable_guest,
|
|||
static bool vmci_guest_personality_initialized;
|
||||
static bool vmci_host_personality_initialized;
|
||||
|
||||
static DEFINE_MUTEX(vmci_vsock_mutex); /* protects vmci_vsock_transport_cb */
|
||||
static vmci_vsock_cb vmci_vsock_transport_cb;
|
||||
static bool vmci_vsock_cb_host_called;
|
||||
|
||||
/*
|
||||
* vmci_get_context_id() - Gets the current context ID.
|
||||
*
|
||||
|
@ -45,6 +49,69 @@ u32 vmci_get_context_id(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(vmci_get_context_id);
|
||||
|
||||
/*
|
||||
* vmci_register_vsock_callback() - Register the VSOCK vmci_transport callback.
|
||||
*
|
||||
* The callback will be called when the first host or guest becomes active,
|
||||
* or if they are already active when this function is called.
|
||||
* To unregister the callback, call this function with NULL parameter.
|
||||
*
|
||||
* Returns 0 on success. -EBUSY if a callback is already registered.
|
||||
*/
|
||||
int vmci_register_vsock_callback(vmci_vsock_cb callback)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&vmci_vsock_mutex);
|
||||
|
||||
if (vmci_vsock_transport_cb && callback) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vmci_vsock_transport_cb = callback;
|
||||
|
||||
if (!vmci_vsock_transport_cb) {
|
||||
vmci_vsock_cb_host_called = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vmci_guest_code_active())
|
||||
vmci_vsock_transport_cb(false);
|
||||
|
||||
if (vmci_host_users() > 0) {
|
||||
vmci_vsock_cb_host_called = true;
|
||||
vmci_vsock_transport_cb(true);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&vmci_vsock_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vmci_register_vsock_callback);
|
||||
|
||||
void vmci_call_vsock_callback(bool is_host)
|
||||
{
|
||||
mutex_lock(&vmci_vsock_mutex);
|
||||
|
||||
if (!vmci_vsock_transport_cb)
|
||||
goto out;
|
||||
|
||||
/* In the host, this function could be called multiple times,
|
||||
* but we want to register it only once.
|
||||
*/
|
||||
if (is_host) {
|
||||
if (vmci_vsock_cb_host_called)
|
||||
goto out;
|
||||
|
||||
vmci_vsock_cb_host_called = true;
|
||||
}
|
||||
|
||||
vmci_vsock_transport_cb(is_host);
|
||||
out:
|
||||
mutex_unlock(&vmci_vsock_mutex);
|
||||
}
|
||||
|
||||
static int __init vmci_drv_init(void)
|
||||
{
|
||||
int vmci_err;
|
||||
|
|
|
@ -36,10 +36,12 @@ extern struct pci_dev *vmci_pdev;
|
|||
|
||||
u32 vmci_get_context_id(void);
|
||||
int vmci_send_datagram(struct vmci_datagram *dg);
|
||||
void vmci_call_vsock_callback(bool is_host);
|
||||
|
||||
int vmci_host_init(void);
|
||||
void vmci_host_exit(void);
|
||||
bool vmci_host_code_active(void);
|
||||
int vmci_host_users(void);
|
||||
|
||||
int vmci_guest_init(void);
|
||||
void vmci_guest_exit(void);
|
||||
|
|
|
@ -637,6 +637,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
|
|||
vmci_dev->iobase + VMCI_CONTROL_ADDR);
|
||||
|
||||
pci_set_drvdata(pdev, vmci_dev);
|
||||
|
||||
vmci_call_vsock_callback(false);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
|
|
|
@ -108,6 +108,11 @@ bool vmci_host_code_active(void)
|
|||
atomic_read(&vmci_host_active_users) > 0);
|
||||
}
|
||||
|
||||
int vmci_host_users(void)
|
||||
{
|
||||
return atomic_read(&vmci_host_active_users);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on open of /dev/vmci.
|
||||
*/
|
||||
|
@ -338,6 +343,8 @@ static int vmci_host_do_init_context(struct vmci_host_dev *vmci_host_dev,
|
|||
vmci_host_dev->ct_type = VMCIOBJ_CONTEXT;
|
||||
atomic_inc(&vmci_host_active_users);
|
||||
|
||||
vmci_call_vsock_callback(true);
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
|
|
|
@ -153,22 +153,22 @@ config IPVLAN_L3S
|
|||
select NET_L3_MASTER_DEV
|
||||
|
||||
config IPVLAN
|
||||
tristate "IP-VLAN support"
|
||||
depends on INET
|
||||
depends on IPV6 || !IPV6
|
||||
---help---
|
||||
This allows one to create virtual devices off of a main interface
|
||||
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
|
||||
on packets. All interfaces (including the main interface) share L2
|
||||
making it transparent to the connected L2 switch.
|
||||
tristate "IP-VLAN support"
|
||||
depends on INET
|
||||
depends on IPV6 || !IPV6
|
||||
---help---
|
||||
This allows one to create virtual devices off of a main interface
|
||||
and packets will be delivered based on the dest L3 (IPv6/IPv4 addr)
|
||||
on packets. All interfaces (including the main interface) share L2
|
||||
making it transparent to the connected L2 switch.
|
||||
|
||||
Ipvlan devices can be added using the "ip" command from the
|
||||
iproute2 package starting with the iproute2-3.19 release:
|
||||
Ipvlan devices can be added using the "ip" command from the
|
||||
iproute2 package starting with the iproute2-3.19 release:
|
||||
|
||||
"ip link add link <main-dev> [ NAME ] type ipvlan"
|
||||
"ip link add link <main-dev> [ NAME ] type ipvlan"
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ipvlan.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ipvlan.
|
||||
|
||||
config IPVTAP
|
||||
tristate "IP-VLAN based tap driver"
|
||||
|
@ -185,11 +185,11 @@ config IPVTAP
|
|||
will be called ipvtap.
|
||||
|
||||
config VXLAN
|
||||
tristate "Virtual eXtensible Local Area Network (VXLAN)"
|
||||
depends on INET
|
||||
select NET_UDP_TUNNEL
|
||||
select GRO_CELLS
|
||||
---help---
|
||||
tristate "Virtual eXtensible Local Area Network (VXLAN)"
|
||||
depends on INET
|
||||
select NET_UDP_TUNNEL
|
||||
select GRO_CELLS
|
||||
---help---
|
||||
This allows one to create vxlan virtual interfaces that provide
|
||||
Layer 2 Networks over Layer 3 Networks. VXLAN is often used
|
||||
to tunnel virtual network infrastructure in virtualized environments.
|
||||
|
@ -200,12 +200,12 @@ config VXLAN
|
|||
will be called vxlan.
|
||||
|
||||
config GENEVE
|
||||
tristate "Generic Network Virtualization Encapsulation"
|
||||
depends on INET
|
||||
depends on IPV6 || !IPV6
|
||||
select NET_UDP_TUNNEL
|
||||
select GRO_CELLS
|
||||
---help---
|
||||
tristate "Generic Network Virtualization Encapsulation"
|
||||
depends on INET
|
||||
depends on IPV6 || !IPV6
|
||||
select NET_UDP_TUNNEL
|
||||
select GRO_CELLS
|
||||
---help---
|
||||
This allows one to create geneve virtual interfaces that provide
|
||||
Layer 2 Networks over Layer 3 Networks. GENEVE is often used
|
||||
to tunnel virtual network infrastructure in virtualized environments.
|
||||
|
@ -244,8 +244,8 @@ config MACSEC
|
|||
config NETCONSOLE
|
||||
tristate "Network console logging support"
|
||||
---help---
|
||||
If you want to log kernel messages over the network, enable this.
|
||||
See <file:Documentation/networking/netconsole.txt> for details.
|
||||
If you want to log kernel messages over the network, enable this.
|
||||
See <file:Documentation/networking/netconsole.txt> for details.
|
||||
|
||||
config NETCONSOLE_DYNAMIC
|
||||
bool "Dynamic reconfiguration of logging targets"
|
||||
|
@ -362,12 +362,12 @@ config NET_VRF
|
|||
support enables VRF devices.
|
||||
|
||||
config VSOCKMON
|
||||
tristate "Virtual vsock monitoring device"
|
||||
depends on VHOST_VSOCK
|
||||
---help---
|
||||
This option enables a monitoring net device for vsock sockets. It is
|
||||
mostly intended for developers or support to debug vsock issues. If
|
||||
unsure, say N.
|
||||
tristate "Virtual vsock monitoring device"
|
||||
depends on VHOST_VSOCK
|
||||
---help---
|
||||
This option enables a monitoring net device for vsock sockets. It is
|
||||
mostly intended for developers or support to debug vsock issues. If
|
||||
unsure, say N.
|
||||
|
||||
endif # NET_CORE
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <linux/in.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -200,6 +202,51 @@ atomic_t netpoll_block_tx = ATOMIC_INIT(0);
|
|||
|
||||
unsigned int bond_net_id __read_mostly;
|
||||
|
||||
static const struct flow_dissector_key flow_keys_bonding_keys[] = {
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_CONTROL,
|
||||
.offset = offsetof(struct flow_keys, control),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_BASIC,
|
||||
.offset = offsetof(struct flow_keys, basic),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
|
||||
.offset = offsetof(struct flow_keys, addrs.v4addrs),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_IPV6_ADDRS,
|
||||
.offset = offsetof(struct flow_keys, addrs.v6addrs),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_TIPC,
|
||||
.offset = offsetof(struct flow_keys, addrs.tipckey),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_PORTS,
|
||||
.offset = offsetof(struct flow_keys, ports),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_ICMP,
|
||||
.offset = offsetof(struct flow_keys, icmp),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_VLAN,
|
||||
.offset = offsetof(struct flow_keys, vlan),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_FLOW_LABEL,
|
||||
.offset = offsetof(struct flow_keys, tags),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_GRE_KEYID,
|
||||
.offset = offsetof(struct flow_keys, keyid),
|
||||
},
|
||||
};
|
||||
|
||||
static struct flow_dissector flow_keys_bonding __read_mostly;
|
||||
|
||||
/*-------------------------- Forward declarations ---------------------------*/
|
||||
|
||||
static int bond_init(struct net_device *bond_dev);
|
||||
|
@ -3252,39 +3299,78 @@ static inline u32 bond_eth_hash(struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk,
|
||||
int *noff, int *proto, bool l34)
|
||||
{
|
||||
const struct ipv6hdr *iph6;
|
||||
const struct iphdr *iph;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
if (unlikely(!pskb_may_pull(skb, *noff + sizeof(*iph))))
|
||||
return false;
|
||||
iph = (const struct iphdr *)(skb->data + *noff);
|
||||
iph_to_flow_copy_v4addrs(fk, iph);
|
||||
*noff += iph->ihl << 2;
|
||||
if (!ip_is_fragment(iph))
|
||||
*proto = iph->protocol;
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
if (unlikely(!pskb_may_pull(skb, *noff + sizeof(*iph6))))
|
||||
return false;
|
||||
iph6 = (const struct ipv6hdr *)(skb->data + *noff);
|
||||
iph_to_flow_copy_v6addrs(fk, iph6);
|
||||
*noff += sizeof(*iph6);
|
||||
*proto = iph6->nexthdr;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (l34 && *proto >= 0)
|
||||
fk->ports.ports = skb_flow_get_ports(skb, *noff, *proto);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Extract the appropriate headers based on bond's xmit policy */
|
||||
static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
|
||||
struct flow_keys *fk)
|
||||
{
|
||||
const struct ipv6hdr *iph6;
|
||||
const struct iphdr *iph;
|
||||
bool l34 = bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34;
|
||||
int noff, proto = -1;
|
||||
|
||||
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23)
|
||||
return skb_flow_dissect_flow_keys(skb, fk, 0);
|
||||
if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) {
|
||||
memset(fk, 0, sizeof(*fk));
|
||||
return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
|
||||
fk, NULL, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
fk->ports.ports = 0;
|
||||
memset(&fk->icmp, 0, sizeof(fk->icmp));
|
||||
noff = skb_network_offset(skb);
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph))))
|
||||
return false;
|
||||
iph = ip_hdr(skb);
|
||||
iph_to_flow_copy_v4addrs(fk, iph);
|
||||
noff += iph->ihl << 2;
|
||||
if (!ip_is_fragment(iph))
|
||||
proto = iph->protocol;
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
if (unlikely(!pskb_may_pull(skb, noff + sizeof(*iph6))))
|
||||
return false;
|
||||
iph6 = ipv6_hdr(skb);
|
||||
iph_to_flow_copy_v6addrs(fk, iph6);
|
||||
noff += sizeof(*iph6);
|
||||
proto = iph6->nexthdr;
|
||||
} else {
|
||||
if (!bond_flow_ip(skb, fk, &noff, &proto, l34))
|
||||
return false;
|
||||
|
||||
/* ICMP error packets contains at least 8 bytes of the header
|
||||
* of the packet which generated the error. Use this information
|
||||
* to correlate ICMP error packets within the same flow which
|
||||
* generated the error.
|
||||
*/
|
||||
if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
|
||||
skb_flow_get_icmp_tci(skb, &fk->icmp, skb->data,
|
||||
skb_transport_offset(skb),
|
||||
skb_headlen(skb));
|
||||
if (proto == IPPROTO_ICMP) {
|
||||
if (!icmp_is_err(fk->icmp.type))
|
||||
return true;
|
||||
|
||||
noff += sizeof(struct icmphdr);
|
||||
} else if (proto == IPPROTO_ICMPV6) {
|
||||
if (!icmpv6_is_err(fk->icmp.type))
|
||||
return true;
|
||||
|
||||
noff += sizeof(struct icmp6hdr);
|
||||
}
|
||||
return bond_flow_ip(skb, fk, &noff, &proto, l34);
|
||||
}
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34 && proto >= 0)
|
||||
fk->ports.ports = skb_flow_get_ports(skb, noff, proto);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3311,10 +3397,14 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
|
|||
return bond_eth_hash(skb);
|
||||
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
|
||||
bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
|
||||
bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23) {
|
||||
hash = bond_eth_hash(skb);
|
||||
else
|
||||
hash = (__force u32)flow.ports.ports;
|
||||
} else {
|
||||
if (flow.icmp.id)
|
||||
memcpy(&hash, &flow.icmp, sizeof(hash));
|
||||
else
|
||||
memcpy(&hash, &flow.ports.ports, sizeof(hash));
|
||||
}
|
||||
hash ^= (__force u32)flow_get_u32_dst(&flow) ^
|
||||
(__force u32)flow_get_u32_src(&flow);
|
||||
hash ^= (hash >> 16);
|
||||
|
@ -4891,6 +4981,10 @@ static int __init bonding_init(void)
|
|||
goto err;
|
||||
}
|
||||
|
||||
skb_flow_dissector_init(&flow_keys_bonding,
|
||||
flow_keys_bonding_keys,
|
||||
ARRAY_SIZE(flow_keys_bonding_keys));
|
||||
|
||||
register_netdevice_notifier(&bond_netdev_notifier);
|
||||
out:
|
||||
return res;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче