Networking changes for 6.6.

Core
 ----
 
  - Increase size limits for to-be-sent skb frag allocations. This
    allows tun, tap devices and packet sockets to better cope with large
    writes operations.
 
  - Store netdevs in an xarray, to simplify iterating over netdevs.
 
  - Refactor nexthop selection for multipath routes.
 
  - Improve sched class lifetime handling.
 
  - Add backup nexthop ID support for bridge.
 
  - Implement drop reasons support in openvswitch.
 
  - Several data races annotations and fixes.
 
  - Constify the sk parameter of routing functions.
 
  - Prepend kernel version to netconsole message.
 
 Protocols
 ---------
 
  - Implement support for TCP probing the peer being under memory
    pressure.
 
  - Remove hard coded limitation on IPv6 specific info placement
    inside the socket struct.
 
  - Get rid of sysctl_tcp_adv_win_scale and use an auto-estimated
    per socket scaling factor.
 
  - Scaling-up the IPv6 expired route GC via a separated list of
    expiring routes.
 
  - In-kernel support for the TLS alert protocol.
 
  - Better support for UDP reuseport with connected sockets.
 
  - Add NEXT-C-SID support for SRv6 End.X behavior, reducing the SR
    header size.
 
  - Get rid of additional ancillary per MPTCP connection struct socket.
 
  - Implement support for BPF-based MPTCP packet schedulers.
 
  - Format MPTCP subtests selftests results in TAP.
 
  - Several new SMC 2.1 features including unique experimental options,
    max connections per lgr negotiation, max links per lgr negotiation.
 
 BPF
 ---
 
  - Multi-buffer support in AF_XDP.
 
  - Add multi uprobe BPF links for attaching multiple uprobes
    and usdt probes, which is significantly faster and saves extra fds.
 
  - Implement an fd-based tc BPF attach API (TCX) and BPF link support on
    top of it.
 
  - Add SO_REUSEPORT support for TC bpf_sk_assign.
 
  - Support new instructions from cpu v4 to simplify the generated code and
    feature completeness, for x86, arm64, riscv64.
 
  - Support defragmenting IPv(4|6) packets in BPF.
 
  - Teach verifier actual bounds of bpf_get_smp_processor_id()
    and fix perf+libbpf issue related to custom section handling.
 
  - Introduce bpf map element count and enable it for all program types.
 
  - Add a BPF hook in sys_socket() to change the protocol ID
    from IPPROTO_TCP to IPPROTO_MPTCP to cover migration for legacy.
 
  - Introduce bpf_me_mcache_free_rcu() and fix OOM under stress.
 
  - Add uprobe support for the bpf_get_func_ip helper.
 
  - Check skb ownership against full socket.
 
  - Support for up to 12 arguments in BPF trampoline.
 
  - Extend link_info for kprobe_multi and perf_event links.
 
 Netfilter
 ---------
 
  - Speed-up process exit by aborting ruleset validation if a
    fatal signal is pending.
 
  - Allow NLA_POLICY_MASK to be used with BE16/BE32 types.
 
 Driver API
 ----------
 
  - Page pool optimizations, to improve data locality and cache usage.
 
  - Introduce ndo_hwtstamp_get() and ndo_hwtstamp_set() to avoid the need
    for raw ioctl() handling in drivers.
 
  - Simplify genetlink dump operations (doit/dumpit) providing them
    the common information already populated in struct genl_info.
 
  - Extend and use the yaml devlink specs to [re]generate the split ops.
 
  - Introduce devlink selective dumps, to allow SF filtering SF based on
    handle and other attributes.
 
  - Add yaml netlink spec for netlink-raw families, allow route, link and
    address related queries via the ynl tool.
 
  - Remove phylink legacy mode support.
 
  - Support offload LED blinking to phy.
 
  - Add devlink port function attributes for IPsec.
 
 New hardware / drivers
 ----------------------
 
  - Ethernet:
    - Broadcom ASP 2.0 (72165) ethernet controller
    - MediaTek MT7988 SoC
    - Texas Instruments AM654 SoC
    - Texas Instruments IEP driver
    - Atheros qca8081 phy
    - Marvell 88Q2110 phy
    - NXP TJA1120 phy
 
  - WiFi:
    - MediaTek mt7981 support
 
  - Can:
    - Kvaser SmartFusion2 PCI Express devices
    - Allwinner T113 controllers
    - Texas Instruments tcan4552/4553 chips
 
  - Bluetooth:
    - Intel Gale Peak
    - Qualcomm WCN3988 and WCN7850
    - NXP AW693 and IW624
    - Mediatek MT2925
 
 Drivers
 -------
 
  - Ethernet NICs:
    - nVidia/Mellanox:
      - mlx5:
        - support UDP encapsulation in packet offload mode
        - IPsec packet offload support in eswitch mode
        - improve aRFS observability by adding new set of counters
        - extends MACsec offload support to cover RoCE traffic
        - dynamic completion EQs
      - mlx4:
        - convert to use auxiliary bus instead of custom interface logic
    - Intel
      - ice:
        - implement switchdev bridge offload, even for LAG interfaces
        - implement SRIOV support for LAG interfaces
      - igc:
        - add support for multiple in-flight TX timestamps
    - Broadcom:
      - bnxt:
        - use the unified RX page pool buffers for XDP and non-XDP
        - use the NAPI skb allocation cache
    - OcteonTX2:
      - support Round Robin scheduling HTB offload
      - TC flower offload support for SPI field
    - Freescale:
      -  add XDP_TX feature support
    - AMD:
      - ionic: add support for PCI FLR event
      - sfc:
        - basic conntrack offload
        - introduce eth, ipv4 and ipv6 pedit offloads
    - ST Microelectronics:
      - stmmac: maximze PTP timestamping resolution
 
  - Virtual NICs:
    - Microsoft vNIC:
      - batch ringing RX queue doorbell on receiving packets
      - add page pool for RX buffers
    - Virtio vNIC:
      - add per queue interrupt coalescing support
    - Google vNIC:
      - add queue-page-list mode support
 
  - Ethernet high-speed switches:
    - nVidia/Mellanox (mlxsw):
      - add port range matching tc-flower offload
      - permit enslavement to netdevices with uppers
 
  - Ethernet embedded switches:
    - Marvell (mv88e6xxx):
      - convert to phylink_pcs
    - Renesas:
      - r8A779fx: add speed change support
      - rzn1: enables vlan support
 
  - Ethernet PHYs:
    - convert mv88e6xxx to phylink_pcs
 
  - WiFi:
    - Qualcomm Wi-Fi 7 (ath12k):
      - extremely High Throughput (EHT) PHY support
    - RealTek (rtl8xxxu):
      - enable AP mode for: RTL8192FU, RTL8710BU (RTL8188GU),
        RTL8192EU and RTL8723BU
    - RealTek (rtw89):
      - Introduce Time Averaged SAR (TAS) support
 
  - Connector:
    - support for event filtering
 
 Signed-off-by: Paolo Abeni <pabeni@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEEg1AjqC77wbdLX2LbKSR5jcyPE6QFAmTt1ZoSHHBhYmVuaUBy
 ZWRoYXQuY29tAAoJECkkeY3MjxOkgFUP/REFaYWdWUvAzmWeezyx9dqgZMfSOjWq
 9QvySiA94OAOcjIYkb7wfzQ5BBAZqaBQ/f8XqWwS1EDDDEBs8sP1cxmABKwW7Hsr
 qFRu2sOqLzKBk223d0jIgEocfQaFpGbF71gXoTlDivBjBi5UxWm9bF0XnbYWcKgO
 /QEvzNosi9uNdi85Fzmv62J6YzAdidEpwGsM7X2CfejwNRmStxAEg/NwvRR0Hyiq
 OJCo97omEgTRaUle8nc64PDx33u4h5kQ1BkaeHEv0rbE3hftFC2YPKn/InmqSFGz
 6ew2xnrGPR37LCuAiCcIIv6yR7K0eu0iYJ7jXwZxBDqxGavEPuwWGBoCP6qFiitH
 ZLWhIrAUrdmSbySkTOCONhJ475qFAuQoYHYpZnX/bJZUHlSsb/9lwDJYJQGpVfd1
 /daqJVSb7lhaifmNO1iNd/ibCIXq9zapwtkRwA897M8GkZBTsnVvazFld1Em+Se3
 Bx6DSDUVBqVQ9fpZG2IAGD6odDwOzC1lF2IoceFvK9Ff6oE0psI+A0qNLMkHxZbW
 Qlo7LsNe53hpoCC+yHTfXX7e/X8eNt0EnCGOQJDusZ0Nr3K7H4LKFA0i8UBUK05n
 4lKnnaSQW7GQgdofLWt103OMDR9GoDxpFsm7b1X9+AEk6Fz6tq50wWYeMZETUKYP
 DCW8VGFOZjZM
 =9CsR
 -----END PGP SIGNATURE-----

Merge tag 'net-next-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next

Pull networking updates from Paolo Abeni:
 "Core:

   - Increase size limits for to-be-sent skb frag allocations. This
     allows tun, tap devices and packet sockets to better cope with
     large writes operations

   - Store netdevs in an xarray, to simplify iterating over netdevs

   - Refactor nexthop selection for multipath routes

   - Improve sched class lifetime handling

   - Add backup nexthop ID support for bridge

   - Implement drop reasons support in openvswitch

   - Several data races annotations and fixes

   - Constify the sk parameter of routing functions

   - Prepend kernel version to netconsole message

  Protocols:

   - Implement support for TCP probing the peer being under memory
     pressure

   - Remove hard coded limitation on IPv6 specific info placement inside
     the socket struct

   - Get rid of sysctl_tcp_adv_win_scale and use an auto-estimated per
     socket scaling factor

   - Scaling-up the IPv6 expired route GC via a separated list of
     expiring routes

   - In-kernel support for the TLS alert protocol

   - Better support for UDP reuseport with connected sockets

   - Add NEXT-C-SID support for SRv6 End.X behavior, reducing the SR
     header size

   - Get rid of additional ancillary per MPTCP connection struct socket

   - Implement support for BPF-based MPTCP packet schedulers

   - Format MPTCP subtests selftests results in TAP

   - Several new SMC 2.1 features including unique experimental options,
     max connections per lgr negotiation, max links per lgr negotiation

  BPF:

   - Multi-buffer support in AF_XDP

   - Add multi uprobe BPF links for attaching multiple uprobes and usdt
     probes, which is significantly faster and saves extra fds

   - Implement an fd-based tc BPF attach API (TCX) and BPF link support
     on top of it

   - Add SO_REUSEPORT support for TC bpf_sk_assign

   - Support new instructions from cpu v4 to simplify the generated code
     and feature completeness, for x86, arm64, riscv64

   - Support defragmenting IPv(4|6) packets in BPF

   - Teach verifier actual bounds of bpf_get_smp_processor_id() and fix
     perf+libbpf issue related to custom section handling

   - Introduce bpf map element count and enable it for all program types

   - Add a BPF hook in sys_socket() to change the protocol ID from
     IPPROTO_TCP to IPPROTO_MPTCP to cover migration for legacy

   - Introduce bpf_me_mcache_free_rcu() and fix OOM under stress

   - Add uprobe support for the bpf_get_func_ip helper

   - Check skb ownership against full socket

   - Support for up to 12 arguments in BPF trampoline

   - Extend link_info for kprobe_multi and perf_event links

  Netfilter:

   - Speed-up process exit by aborting ruleset validation if a fatal
     signal is pending

   - Allow NLA_POLICY_MASK to be used with BE16/BE32 types

  Driver API:

   - Page pool optimizations, to improve data locality and cache usage

   - Introduce ndo_hwtstamp_get() and ndo_hwtstamp_set() to avoid the
     need for raw ioctl() handling in drivers

   - Simplify genetlink dump operations (doit/dumpit) providing them the
     common information already populated in struct genl_info

   - Extend and use the yaml devlink specs to [re]generate the split ops

   - Introduce devlink selective dumps, to allow SF filtering SF based
     on handle and other attributes

   - Add yaml netlink spec for netlink-raw families, allow route, link
     and address related queries via the ynl tool

   - Remove phylink legacy mode support

   - Support offload LED blinking to phy

   - Add devlink port function attributes for IPsec

  New hardware / drivers:

   - Ethernet:
      - Broadcom ASP 2.0 (72165) ethernet controller
      - MediaTek MT7988 SoC
      - Texas Instruments AM654 SoC
      - Texas Instruments IEP driver
      - Atheros qca8081 phy
      - Marvell 88Q2110 phy
      - NXP TJA1120 phy

   - WiFi:
      - MediaTek mt7981 support

   - Can:
      - Kvaser SmartFusion2 PCI Express devices
      - Allwinner T113 controllers
      - Texas Instruments tcan4552/4553 chips

   - Bluetooth:
      - Intel Gale Peak
      - Qualcomm WCN3988 and WCN7850
      - NXP AW693 and IW624
      - Mediatek MT2925

  Drivers:

   - Ethernet NICs:
      - nVidia/Mellanox:
         - mlx5:
            - support UDP encapsulation in packet offload mode
            - IPsec packet offload support in eswitch mode
            - improve aRFS observability by adding new set of counters
            - extends MACsec offload support to cover RoCE traffic
            - dynamic completion EQs
         - mlx4:
            - convert to use auxiliary bus instead of custom interface
              logic
      - Intel
         - ice:
            - implement switchdev bridge offload, even for LAG
              interfaces
            - implement SRIOV support for LAG interfaces
         - igc:
            - add support for multiple in-flight TX timestamps
      - Broadcom:
         - bnxt:
            - use the unified RX page pool buffers for XDP and non-XDP
            - use the NAPI skb allocation cache
      - OcteonTX2:
         - support Round Robin scheduling HTB offload
         - TC flower offload support for SPI field
      - Freescale:
         - add XDP_TX feature support
      - AMD:
         - ionic: add support for PCI FLR event
         - sfc:
            - basic conntrack offload
            - introduce eth, ipv4 and ipv6 pedit offloads
      - ST Microelectronics:
         - stmmac: maximze PTP timestamping resolution

   - Virtual NICs:
      - Microsoft vNIC:
         - batch ringing RX queue doorbell on receiving packets
         - add page pool for RX buffers
      - Virtio vNIC:
         - add per queue interrupt coalescing support
      - Google vNIC:
         - add queue-page-list mode support

   - Ethernet high-speed switches:
      - nVidia/Mellanox (mlxsw):
         - add port range matching tc-flower offload
         - permit enslavement to netdevices with uppers

   - Ethernet embedded switches:
      - Marvell (mv88e6xxx):
         - convert to phylink_pcs
      - Renesas:
         - r8A779fx: add speed change support
         - rzn1: enables vlan support

   - Ethernet PHYs:
      - convert mv88e6xxx to phylink_pcs

   - WiFi:
      - Qualcomm Wi-Fi 7 (ath12k):
         - extremely High Throughput (EHT) PHY support
      - RealTek (rtl8xxxu):
         - enable AP mode for: RTL8192FU, RTL8710BU (RTL8188GU),
           RTL8192EU and RTL8723BU
      - RealTek (rtw89):
         - Introduce Time Averaged SAR (TAS) support

   - Connector:
      - support for event filtering"

* tag 'net-next-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1806 commits)
  net: ethernet: mtk_wed: minor change in wed_{tx,rx}info_show
  net: ethernet: mtk_wed: add some more info in wed_txinfo_show handler
  net: stmmac: clarify difference between "interface" and "phy_interface"
  r8152: add vendor/device ID pair for D-Link DUB-E250
  devlink: move devlink_notify_register/unregister() to dev.c
  devlink: move small_ops definition into netlink.c
  devlink: move tracepoint definitions into core.c
  devlink: push linecard related code into separate file
  devlink: push rate related code into separate file
  devlink: push trap related code into separate file
  devlink: use tracepoint_enabled() helper
  devlink: push region related code into separate file
  devlink: push param related code into separate file
  devlink: push resource related code into separate file
  devlink: push dpipe related code into separate file
  devlink: move and rename devlink_dpipe_send_and_alloc_skb() helper
  devlink: push shared buffer related code into separate file
  devlink: push port related code into separate file
  devlink: push object register/unregister notifications into separate helpers
  inet: fix IP_TRANSPARENT error handling
  ...
This commit is contained in:
Linus Torvalds 2023-08-29 11:33:01 -07:00
commit bd6c11bc43
1855 changed files with 109685 additions and 46162 deletions

View File

@ -140,11 +140,6 @@ A: Because if we picked one-to-one relationship to x64 it would have made
it more complicated to support on arm64 and other archs. Also it
needs div-by-zero runtime check.
Q: Why there is no BPF_SDIV for signed divide operation?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because it would be rarely used. llvm errors in such case and
prints a suggestion to use unsigned divide instead.
Q: Why BPF has implicit prologue and epilogue?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because architectures like sparc have register windows and in general

View File

@ -635,12 +635,12 @@ test coverage.
Q: clang flag for target bpf?
-----------------------------
Q: In some cases clang flag ``-target bpf`` is used but in other cases the
Q: In some cases clang flag ``--target=bpf`` is used but in other cases the
default clang target, which matches the underlying architecture, is used.
What is the difference and when I should use which?
A: Although LLVM IR generation and optimization try to stay architecture
independent, ``-target <arch>`` still has some impact on generated code:
independent, ``--target=<arch>`` still has some impact on generated code:
- BPF program may recursively include header file(s) with file scope
inline assembly codes. The default target can handle this well,
@ -658,7 +658,7 @@ independent, ``-target <arch>`` still has some impact on generated code:
The clang option ``-fno-jump-tables`` can be used to disable
switch table generation.
- For clang ``-target bpf``, it is guaranteed that pointer or long /
- For clang ``--target=bpf``, it is guaranteed that pointer or long /
unsigned long types will always have a width of 64 bit, no matter
whether underlying clang binary or default target (or kernel) is
32 bit. However, when native clang target is used, then it will
@ -668,7 +668,7 @@ independent, ``-target <arch>`` still has some impact on generated code:
while the BPF LLVM back end still operates in 64 bit. The native
target is mostly needed in tracing for the case of walking ``pt_regs``
or other kernel structures where CPU's register width matters.
Otherwise, ``clang -target bpf`` is generally recommended.
Otherwise, ``clang --target=bpf`` is generally recommended.
You should use default target when:
@ -685,7 +685,7 @@ when:
into these structures is verified by the BPF verifier and may result
in verification failures if the native architecture is not aligned with
the BPF architecture, e.g. 64-bit. An example of this is
BPF_PROG_TYPE_SK_MSG require ``-target bpf``
BPF_PROG_TYPE_SK_MSG require ``--target=bpf``
.. Links

View File

@ -990,7 +990,7 @@ format.::
} g2;
int main() { return 0; }
int test() { return 0; }
-bash-4.4$ clang -c -g -O2 -target bpf t2.c
-bash-4.4$ clang -c -g -O2 --target=bpf t2.c
-bash-4.4$ readelf -S t2.o
......
[ 8] .BTF PROGBITS 0000000000000000 00000247
@ -1000,7 +1000,7 @@ format.::
[10] .rel.BTF.ext REL 0000000000000000 000007e0
0000000000000040 0000000000000010 16 9 8
......
-bash-4.4$ clang -S -g -O2 -target bpf t2.c
-bash-4.4$ clang -S -g -O2 --target=bpf t2.c
-bash-4.4$ cat t2.s
......
.section .BTF,"",@progbits

View File

@ -12,9 +12,9 @@ that goes into great technical depth about the BPF Architecture.
.. toctree::
:maxdepth: 1
instruction-set
verifier
libbpf/index
standardization/index
btf
faq
syscall_api
@ -29,7 +29,6 @@ that goes into great technical depth about the BPF Architecture.
bpf_licensing
test_debug
clang-notes
linux-notes
other
redirect

View File

@ -28,7 +28,7 @@ For example, for the following code::
return g1 + g2 + l1 + l2;
}
Compiled with ``clang -target bpf -O2 -c test.c``, the following is
Compiled with ``clang --target=bpf -O2 -c test.c``, the following is
the code with ``llvm-objdump -dr test.o``::
0: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
@ -157,7 +157,7 @@ and ``call`` instructions. For example::
return gfunc(a, b) + lfunc(a, b) + global;
}
Compiled with ``clang -target bpf -O2 -c test.c``, we will have
Compiled with ``clang --target=bpf -O2 -c test.c``, we will have
following code with `llvm-objdump -dr test.o``::
Disassembly of section .text:
@ -203,7 +203,7 @@ The following is an example to show how R_BPF_64_ABS64 could be generated::
int global() { return 0; }
struct t { void *g; } gbl = { global };
Compiled with ``clang -target bpf -O2 -g -c test.c``, we will see a
Compiled with ``clang --target=bpf -O2 -g -c test.c``, we will see a
relocation below in ``.data`` section with command
``llvm-readelf -r test.o``::

View File

@ -0,0 +1,18 @@
.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
===================
BPF Standardization
===================
This directory contains documents that are being iterated on as part of the BPF
standardization effort with the IETF. See the `IETF BPF Working Group`_ page
for the working group charter, documents, and more.
.. toctree::
:maxdepth: 1
instruction-set
linux-notes
.. Links:
.. _IETF BPF Working Group: https://datatracker.ietf.org/wg/bpf/about/

View File

@ -10,9 +10,92 @@ This document specifies version 1.0 of the eBPF instruction set.
Documentation conventions
=========================
For brevity, this document uses the type notion "u64", "u32", etc.
to mean an unsigned integer whose width is the specified number of bits,
and "s32", etc. to mean a signed integer of the specified number of bits.
For brevity and consistency, this document refers to families
of types using a shorthand syntax and refers to several expository,
mnemonic functions when describing the semantics of instructions.
The range of valid values for those types and the semantics of those
functions are defined in the following subsections.
Types
-----
This document refers to integer types with the notation `SN` to specify
a type's signedness (`S`) and bit width (`N`), respectively.
.. table:: Meaning of signedness notation.
==== =========
`S` Meaning
==== =========
`u` unsigned
`s` signed
==== =========
.. table:: Meaning of bit-width notation.
===== =========
`N` Bit width
===== =========
`8` 8 bits
`16` 16 bits
`32` 32 bits
`64` 64 bits
`128` 128 bits
===== =========
For example, `u32` is a type whose valid values are all the 32-bit unsigned
numbers and `s16` is a types whose valid values are all the 16-bit signed
numbers.
Functions
---------
* `htobe16`: Takes an unsigned 16-bit number in host-endian format and
returns the equivalent number as an unsigned 16-bit number in big-endian
format.
* `htobe32`: Takes an unsigned 32-bit number in host-endian format and
returns the equivalent number as an unsigned 32-bit number in big-endian
format.
* `htobe64`: Takes an unsigned 64-bit number in host-endian format and
returns the equivalent number as an unsigned 64-bit number in big-endian
format.
* `htole16`: Takes an unsigned 16-bit number in host-endian format and
returns the equivalent number as an unsigned 16-bit number in little-endian
format.
* `htole32`: Takes an unsigned 32-bit number in host-endian format and
returns the equivalent number as an unsigned 32-bit number in little-endian
format.
* `htole64`: Takes an unsigned 64-bit number in host-endian format and
returns the equivalent number as an unsigned 64-bit number in little-endian
format.
* `bswap16`: Takes an unsigned 16-bit number in either big- or little-endian
format and returns the equivalent number with the same bit width but
opposite endianness.
* `bswap32`: Takes an unsigned 32-bit number in either big- or little-endian
format and returns the equivalent number with the same bit width but
opposite endianness.
* `bswap64`: Takes an unsigned 64-bit number in either big- or little-endian
format and returns the equivalent number with the same bit width but
opposite endianness.
Definitions
-----------
.. glossary::
Sign Extend
To `sign extend an` ``X`` `-bit number, A, to a` ``Y`` `-bit number, B ,` means to
#. Copy all ``X`` bits from `A` to the lower ``X`` bits of `B`.
#. Set the value of the remaining ``Y`` - ``X`` bits of `B` to the value of
the most-significant bit of `A`.
.. admonition:: Example
Sign extend an 8-bit number ``A`` to a 16-bit number ``B`` on a big-endian platform:
::
A: 10000110
B: 11111111 10000110
Registers and calling convention
================================
@ -154,24 +237,27 @@ otherwise identical operations.
The 'code' field encodes the operation as below, where 'src' and 'dst' refer
to the values of the source and destination registers, respectively.
======== ===== ==========================================================
code value description
======== ===== ==========================================================
BPF_ADD 0x00 dst += src
BPF_SUB 0x10 dst -= src
BPF_MUL 0x20 dst \*= src
BPF_DIV 0x30 dst = (src != 0) ? (dst / src) : 0
BPF_OR 0x40 dst \|= src
BPF_AND 0x50 dst &= src
BPF_LSH 0x60 dst <<= (src & mask)
BPF_RSH 0x70 dst >>= (src & mask)
BPF_NEG 0x80 dst = ~src
BPF_MOD 0x90 dst = (src != 0) ? (dst % src) : dst
BPF_XOR 0xa0 dst ^= src
BPF_MOV 0xb0 dst = src
BPF_ARSH 0xc0 sign extending dst >>= (src & mask)
BPF_END 0xd0 byte swap operations (see `Byte swap instructions`_ below)
======== ===== ==========================================================
========= ===== ======= ==========================================================
code value offset description
========= ===== ======= ==========================================================
BPF_ADD 0x00 0 dst += src
BPF_SUB 0x10 0 dst -= src
BPF_MUL 0x20 0 dst \*= src
BPF_DIV 0x30 0 dst = (src != 0) ? (dst / src) : 0
BPF_SDIV 0x30 1 dst = (src != 0) ? (dst s/ src) : 0
BPF_OR 0x40 0 dst \|= src
BPF_AND 0x50 0 dst &= src
BPF_LSH 0x60 0 dst <<= (src & mask)
BPF_RSH 0x70 0 dst >>= (src & mask)
BPF_NEG 0x80 0 dst = -dst
BPF_MOD 0x90 0 dst = (src != 0) ? (dst % src) : dst
BPF_SMOD 0x90 1 dst = (src != 0) ? (dst s% src) : dst
BPF_XOR 0xa0 0 dst ^= src
BPF_MOV 0xb0 0 dst = src
BPF_MOVSX 0xb0 8/16/32 dst = (s8,s16,s32)src
BPF_ARSH 0xc0 0 :term:`sign extending<Sign Extend>` dst >>= (src & mask)
BPF_END 0xd0 0 byte swap operations (see `Byte swap instructions`_ below)
========= ===== ======= ==========================================================
Underflow and overflow are allowed during arithmetic operations, meaning
the 64-bit or 32-bit value will wrap. If eBPF program execution would
@ -198,47 +284,75 @@ where '(u32)' indicates that the upper 32 bits are zeroed.
dst = dst ^ imm32
Also note that the division and modulo operations are unsigned. Thus, for
``BPF_ALU``, 'imm' is first interpreted as an unsigned 32-bit value, whereas
for ``BPF_ALU64``, 'imm' is first sign extended to 64 bits and the result
interpreted as an unsigned 64-bit value. There are no instructions for
signed division or modulo.
Note that most instructions have instruction offset of 0. Only three instructions
(``BPF_SDIV``, ``BPF_SMOD``, ``BPF_MOVSX``) have a non-zero offset.
The division and modulo operations support both unsigned and signed flavors.
For unsigned operations (``BPF_DIV`` and ``BPF_MOD``), for ``BPF_ALU``,
'imm' is interpreted as a 32-bit unsigned value. For ``BPF_ALU64``,
'imm' is first :term:`sign extended<Sign Extend>` from 32 to 64 bits, and then
interpreted as a 64-bit unsigned value.
For signed operations (``BPF_SDIV`` and ``BPF_SMOD``), for ``BPF_ALU``,
'imm' is interpreted as a 32-bit signed value. For ``BPF_ALU64``, 'imm'
is first :term:`sign extended<Sign Extend>` from 32 to 64 bits, and then
interpreted as a 64-bit signed value.
The ``BPF_MOVSX`` instruction does a move operation with sign extension.
``BPF_ALU | BPF_MOVSX`` :term:`sign extends<Sign Extend>` 8-bit and 16-bit operands into 32
bit operands, and zeroes the remaining upper 32 bits.
``BPF_ALU64 | BPF_MOVSX`` :term:`sign extends<Sign Extend>` 8-bit, 16-bit, and 32-bit
operands into 64 bit operands.
Shift operations use a mask of 0x3F (63) for 64-bit operations and 0x1F (31)
for 32-bit operations.
Byte swap instructions
~~~~~~~~~~~~~~~~~~~~~~
----------------------
The byte swap instructions use an instruction class of ``BPF_ALU`` and a 4-bit
'code' field of ``BPF_END``.
The byte swap instructions use instruction classes of ``BPF_ALU`` and ``BPF_ALU64``
and a 4-bit 'code' field of ``BPF_END``.
The byte swap instructions operate on the destination register
only and do not use a separate source register or immediate value.
The 1-bit source operand field in the opcode is used to select what byte
order the operation convert from or to:
For ``BPF_ALU``, the 1-bit source operand field in the opcode is used to
select what byte order the operation converts from or to. For
``BPF_ALU64``, the 1-bit source operand field in the opcode is reserved
and must be set to 0.
========= ===== =================================================
source value description
========= ===== =================================================
BPF_TO_LE 0x00 convert between host byte order and little endian
BPF_TO_BE 0x08 convert between host byte order and big endian
========= ===== =================================================
========= ========= ===== =================================================
class source value description
========= ========= ===== =================================================
BPF_ALU BPF_TO_LE 0x00 convert between host byte order and little endian
BPF_ALU BPF_TO_BE 0x08 convert between host byte order and big endian
BPF_ALU64 Reserved 0x00 do byte swap unconditionally
========= ========= ===== =================================================
The 'imm' field encodes the width of the swap operations. The following widths
are supported: 16, 32 and 64.
Examples:
``BPF_ALU | BPF_TO_LE | BPF_END`` with imm = 16 means::
``BPF_ALU | BPF_TO_LE | BPF_END`` with imm = 16/32/64 means::
dst = htole16(dst)
dst = htole32(dst)
dst = htole64(dst)
``BPF_ALU | BPF_TO_BE | BPF_END`` with imm = 64 means::
``BPF_ALU | BPF_TO_BE | BPF_END`` with imm = 16/32/64 means::
dst = htobe16(dst)
dst = htobe32(dst)
dst = htobe64(dst)
``BPF_ALU64 | BPF_TO_LE | BPF_END`` with imm = 16/32/64 means::
dst = bswap16(dst)
dst = bswap32(dst)
dst = bswap64(dst)
Jump instructions
-----------------
@ -249,7 +363,8 @@ The 'code' field encodes the operation as below:
======== ===== === =========================================== =========================================
code value src description notes
======== ===== === =========================================== =========================================
BPF_JA 0x0 0x0 PC += offset BPF_JMP only
BPF_JA 0x0 0x0 PC += offset BPF_JMP class
BPF_JA 0x0 0x0 PC += imm BPF_JMP32 class
BPF_JEQ 0x1 any PC += offset if dst == src
BPF_JGT 0x2 any PC += offset if dst > src unsigned
BPF_JGE 0x3 any PC += offset if dst >= src unsigned
@ -278,6 +393,19 @@ Example:
where 's>=' indicates a signed '>=' comparison.
``BPF_JA | BPF_K | BPF_JMP32`` (0x06) means::
gotol +imm
where 'imm' means the branch offset comes from insn 'imm' field.
Note that there are two flavors of ``BPF_JA`` instructions. The
``BPF_JMP`` class permits a 16-bit jump offset specified by the 'offset'
field, whereas the ``BPF_JMP32`` class permits a 32-bit jump offset
specified by the 'imm' field. A > 16-bit conditional jump may be
converted to a < 16-bit conditional jump plus a 32-bit unconditional
jump.
Helper functions
~~~~~~~~~~~~~~~~
@ -320,6 +448,7 @@ The mode modifier is one of:
BPF_ABS 0x20 legacy BPF packet access (absolute) `Legacy BPF Packet access instructions`_
BPF_IND 0x40 legacy BPF packet access (indirect) `Legacy BPF Packet access instructions`_
BPF_MEM 0x60 regular load and store operations `Regular load and store operations`_
BPF_MEMSX 0x80 sign-extension load operations `Sign-extension load operations`_
BPF_ATOMIC 0xc0 atomic operations `Atomic operations`_
============= ===== ==================================== =============
@ -350,9 +479,23 @@ instructions that transfer data between a register and memory.
``BPF_MEM | <size> | BPF_LDX`` means::
dst = *(size *) (src + offset)
dst = *(unsigned size *) (src + offset)
Where size is one of: ``BPF_B``, ``BPF_H``, ``BPF_W``, or ``BPF_DW``.
Where size is one of: ``BPF_B``, ``BPF_H``, ``BPF_W``, or ``BPF_DW`` and
'unsigned size' is one of u8, u16, u32 or u64.
Sign-extension load operations
------------------------------
The ``BPF_MEMSX`` mode modifier is used to encode :term:`sign-extension<Sign Extend>` load
instructions that transfer data between a register and memory.
``BPF_MEMSX | <size> | BPF_LDX`` means::
dst = *(signed size *) (src + offset)
Where size is one of: ``BPF_B``, ``BPF_H`` or ``BPF_W``, and
'signed size' is one of s8, s16 or s32.
Atomic operations
-----------------

View File

@ -45,7 +45,8 @@ On Linux, this integer is a BTF ID.
Legacy BPF Packet access instructions
=====================================
As mentioned in the `ISA standard documentation <instruction-set.rst#legacy-bpf-packet-access-instructions>`_,
As mentioned in the `ISA standard documentation
<instruction-set.html#legacy-bpf-packet-access-instructions>`_,
Linux has special eBPF instructions for access to packet data that have been
carried over from classic BPF to retain the performance of legacy socket
filters running in the eBPF interpreter.

View File

@ -67,10 +67,11 @@ Globals
kernel-policy
~~~~~~~~~~~~~
Defines if the kernel validation policy is per operation (``per-op``)
or for the entire family (``global``). New families should use ``per-op``
(default) to be able to narrow down the attributes accepted by a specific
command.
Defines whether the kernel validation policy is ``global`` i.e. the same for all
operations of the family, defined for each operation individually - ``per-op``,
or separately for each operation and operation type (do vs dump) - ``split``.
New families should use ``per-op`` (default) to be able to narrow down the
attributes accepted by a specific command.
checks
------

View File

@ -19,12 +19,14 @@ properties:
- qcom,qca2066-bt
- qcom,qca6174-bt
- qcom,qca9377-bt
- qcom,wcn3988-bt
- qcom,wcn3990-bt
- qcom,wcn3991-bt
- qcom,wcn3998-bt
- qcom,qca6390-bt
- qcom,wcn6750-bt
- qcom,wcn6855-bt
- qcom,wcn7850-bt
enable-gpios:
maxItems: 1
@ -57,6 +59,9 @@ properties:
vddaon-supply:
description: VDD_AON supply regulator handle
vdddig-supply:
description: VDD_DIG supply regulator handle
vddbtcxmx-supply:
description: VDD_BT_CXMX supply regulator handle
@ -72,6 +77,9 @@ properties:
vddrfa1p2-supply:
description: VDD_RFA_1P2 supply regulator handle
vddrfa1p9-supply:
description: VDD_RFA_1P9 supply regulator handle
vddrfa2p2-supply:
description: VDD_RFA_2P2 supply regulator handle
@ -111,6 +119,7 @@ allOf:
compatible:
contains:
enum:
- qcom,wcn3988-bt
- qcom,wcn3990-bt
- qcom,wcn3991-bt
- qcom,wcn3998-bt
@ -155,6 +164,22 @@ allOf:
- vddrfa0p8-supply
- vddrfa1p2-supply
- vddrfa1p7-supply
- if:
properties:
compatible:
contains:
enum:
- qcom,wcn7850-bt
then:
required:
- enable-gpios
- swctrl-gpios
- vddio-supply
- vddaon-supply
- vdddig-supply
- vddrfa0p8-supply
- vddrfa1p2-supply
- vddrfa1p9-supply
examples:
- |

View File

@ -0,0 +1,155 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/brcm,asp-v2.0.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom ASP 2.0 Ethernet controller
maintainers:
- Justin Chen <justin.chen@broadcom.com>
- Florian Fainelli <florian.fainelli@broadcom.com>
description: Broadcom Ethernet controller first introduced with 72165
properties:
compatible:
oneOf:
- items:
- enum:
- brcm,bcm74165-asp
- const: brcm,asp-v2.1
- items:
- enum:
- brcm,bcm72165-asp
- const: brcm,asp-v2.0
"#address-cells":
const: 1
"#size-cells":
const: 1
reg:
maxItems: 1
ranges: true
interrupts:
minItems: 1
items:
- description: RX/TX interrupt
- description: Port 0 Wake-on-LAN
- description: Port 1 Wake-on-LAN
clocks:
maxItems: 1
ethernet-ports:
type: object
properties:
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^port@[0-9]+$":
type: object
$ref: ethernet-controller.yaml#
unevaluatedProperties: false
properties:
reg:
maxItems: 1
description: Port number
brcm,channel:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
ASP Channel Number
The depacketizer channel that consumes packets from
the unimac/port.
required:
- reg
- brcm,channel
additionalProperties: false
patternProperties:
"^mdio@[0-9a-f]+$":
type: object
$ref: brcm,unimac-mdio.yaml
description:
ASP internal UniMAC MDIO bus
required:
- compatible
- reg
- interrupts
- clocks
- ranges
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
ethernet@9c00000 {
compatible = "brcm,bcm72165-asp", "brcm,asp-v2.0";
reg = <0x9c00000 0x1fff14>;
interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
ranges = <0x0 0x9c00000 0x1fff14>;
clocks = <&scmi 14>;
#address-cells = <1>;
#size-cells = <1>;
mdio@c614 {
compatible = "brcm,asp-v2.0-mdio";
reg = <0xc614 0x8>;
reg-names = "mdio";
#address-cells = <1>;
#size-cells = <0>;
phy0: ethernet-phy@1 {
reg = <1>;
};
};
mdio@ce14 {
compatible = "brcm,asp-v2.0-mdio";
reg = <0xce14 0x8>;
reg-names = "mdio";
#address-cells = <1>;
#size-cells = <0>;
phy1: ethernet-phy@1 {
reg = <1>;
};
};
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
brcm,channel = <8>;
phy-mode = "rgmii";
phy-handle = <&phy0>;
};
port@1 {
reg = <1>;
brcm,channel = <9>;
phy-mode = "rgmii";
phy-handle = <&phy1>;
};
};
};

View File

@ -22,6 +22,8 @@ properties:
- brcm,genet-mdio-v3
- brcm,genet-mdio-v4
- brcm,genet-mdio-v5
- brcm,asp-v2.0-mdio
- brcm,asp-v2.1-mdio
- brcm,unimac-mdio
reg:

View File

@ -21,6 +21,7 @@ properties:
- const: allwinner,sun4i-a10-can
- const: allwinner,sun4i-a10-can
- const: allwinner,sun8i-r40-can
- const: allwinner,sun20i-d1-can
reg:
maxItems: 1
@ -37,8 +38,9 @@ properties:
if:
properties:
compatible:
contains:
const: allwinner,sun8i-r40-can
enum:
- allwinner,sun8i-r40-can
- allwinner,sun20i-d1-can
then:
required:

View File

@ -122,8 +122,6 @@ required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
- clocks
- clock-names
- bosch,mram-cfg
@ -132,6 +130,7 @@ additionalProperties: false
examples:
- |
// Example with interrupts
#include <dt-bindings/clock/imx6sx-clock.h>
can@20e8000 {
compatible = "bosch,m_can";
@ -149,4 +148,21 @@ examples:
};
};
- |
// Example with timer polling
#include <dt-bindings/clock/imx6sx-clock.h>
can@20e8000 {
compatible = "bosch,m_can";
reg = <0x020e8000 0x4000>, <0x02298000 0x4000>;
reg-names = "m_can", "message_ram";
clocks = <&clks IMX6SX_CLK_CANFD>,
<&clks IMX6SX_CLK_CANFD>;
clock-names = "hclk", "cclk";
bosch,mram-cfg = <0x0 0 0 32 0 0 0 1>;
can-transceiver {
max-bitrate = <5000000>;
};
};
...

View File

@ -4,7 +4,10 @@ Texas Instruments TCAN4x5x CAN Controller
This file provides device node information for the TCAN4x5x interface contains.
Required properties:
- compatible: "ti,tcan4x5x"
- compatible:
"ti,tcan4552", "ti,tcan4x5x"
"ti,tcan4553", "ti,tcan4x5x" or
"ti,tcan4x5x"
- reg: 0
- #address-cells: 1
- #size-cells: 0
@ -21,8 +24,10 @@ Optional properties:
- reset-gpios: Hardwired output GPIO. If not defined then software
reset.
- device-state-gpios: Input GPIO that indicates if the device is in
a sleep state or if the device is active.
- device-wake-gpios: Wake up GPIO to wake up the TCAN device.
a sleep state or if the device is active. Not
available with tcan4552/4553.
- device-wake-gpios: Wake up GPIO to wake up the TCAN device. Not
available with tcan4552/4553.
Example:
tcan4x5x: tcan4x5x@0 {

View File

@ -46,6 +46,9 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: CAN Tx mailbox buffer count (CAN FD)
resets:
maxItems: 1
required:
- compatible
- reg

View File

@ -36,7 +36,7 @@ additionalProperties: true
$defs:
ethernet-ports:
description: A DSA switch without any extra port properties
$ref: '#/'
$ref: '#'
patternProperties:
"^(ethernet-)?ports$":

View File

@ -20,7 +20,7 @@ which is at a different MDIO base address in different switch families.
6171, 6172, 6175, 6176, 6185, 6240, 6320, 6321,
6341, 6350, 6351, 6352
- "marvell,mv88e6190" : Switch has base address 0x00. Use with models:
6163, 6190, 6190X, 6191, 6290, 6390, 6390X
6190, 6190X, 6191, 6290, 6361, 6390, 6390X
- "marvell,mv88e6250" : Switch has base address 0x08 or 0x18. Use with model:
6220, 6250

View File

@ -66,6 +66,7 @@ properties:
- mii
- gmii
- sgmii
- psgmii
- qsgmii
- qusgmii
- tbi

View File

@ -19,10 +19,12 @@ properties:
enum:
- mediatek,mt2701-eth
- mediatek,mt7623-eth
- mediatek,mt7621-eth
- mediatek,mt7622-eth
- mediatek,mt7629-eth
- mediatek,mt7981-eth
- mediatek,mt7986-eth
- mediatek,mt7988-eth
- ralink,rt5350-eth
reg:
@ -32,7 +34,7 @@ properties:
clock-names: true
interrupts:
minItems: 3
minItems: 1
maxItems: 4
power-domains:
@ -60,6 +62,12 @@ properties:
Phandle to the mediatek hifsys controller used to provide various clocks
and reset to the system.
mediatek,infracfg:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon node that handles the path from GMAC to
PHY variants.
mediatek,sgmiisys:
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
@ -121,6 +129,8 @@ allOf:
- const: gp1
- const: gp2
mediatek,infracfg: false
mediatek,pctl:
$ref: /schemas/types.yaml#/definitions/phandle
description:
@ -131,6 +141,32 @@ allOf:
mediatek,wed-pcie: false
- if:
properties:
compatible:
contains:
enum:
- mediatek,mt7621-eth
then:
properties:
interrupts:
maxItems: 1
clocks:
minItems: 2
maxItems: 2
clock-names:
items:
- const: ethif
- const: fe
mediatek,infracfg: false
mediatek,wed: false
mediatek,wed-pcie: false
- if:
properties:
compatible:
@ -159,6 +195,8 @@ allOf:
- const: sgmii_ck
- const: eth2pll
mediatek,infracfg: false
mediatek,sgmiisys:
minItems: 1
maxItems: 1
@ -204,12 +242,6 @@ allOf:
- const: sgmii_ck
- const: eth2pll
mediatek,infracfg:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon node that handles the path from GMAC to
PHY variants.
mediatek,sgmiisys:
minItems: 2
maxItems: 2
@ -250,6 +282,8 @@ allOf:
- const: netsys0
- const: netsys1
mediatek,infracfg: false
mediatek,sgmiisys:
minItems: 2
maxItems: 2
@ -286,6 +320,67 @@ allOf:
- const: netsys0
- const: netsys1
mediatek,infracfg: false
mediatek,sgmiisys:
minItems: 2
maxItems: 2
- if:
properties:
compatible:
contains:
const: mediatek,mt7988-eth
then:
properties:
interrupts:
minItems: 4
clocks:
minItems: 34
maxItems: 34
clock-names:
items:
- const: crypto
- const: fe
- const: gp2
- const: gp1
- const: gp3
- const: ethwarp_wocpu2
- const: ethwarp_wocpu1
- const: ethwarp_wocpu0
- const: esw
- const: netsys0
- const: netsys1
- const: sgmii_tx250m
- const: sgmii_rx250m
- const: sgmii2_tx250m
- const: sgmii2_rx250m
- const: top_usxgmii0_sel
- const: top_usxgmii1_sel
- const: top_sgm0_sel
- const: top_sgm1_sel
- const: top_xfi_phy0_xtal_sel
- const: top_xfi_phy1_xtal_sel
- const: top_eth_gmii_sel
- const: top_eth_refck_50m_sel
- const: top_eth_sys_200m_sel
- const: top_eth_sys_sel
- const: top_eth_xgmii_sel
- const: top_eth_mii_sel
- const: top_netsys_sel
- const: top_netsys_500m_sel
- const: top_netsys_pao_2x_sel
- const: top_netsys_sync_250m_sel
- const: top_netsys_ppefb_250m_sel
- const: top_netsys_warp_sel
- const: wocpu1
- const: wocpu0
- const: xgp1
- const: xgp2
- const: xgp3
mediatek,sgmiisys:
minItems: 2
maxItems: 2

View File

@ -52,6 +52,40 @@ properties:
for a timer.
type: boolean
motorcomm,rx-clk-drv-microamp:
description: |
drive strength of rx_clk rgmii pad.
The YT8531 RGMII LDO voltage supports 1.8V/3.3V, and the LDO voltage can
be configured with hardware pull-up resistors to match the SOC voltage
(usually 1.8V).
The software can read the registers to obtain the LDO voltage and configure
the legal drive strength(curren).
=====================================================
| voltage | current Available (uA) |
| 1.8v | 1200 2100 2700 2910 3110 3600 3970 4350 |
| 3.3v | 3070 4080 4370 4680 5020 5450 5740 6140 |
=====================================================
enum: [ 1200, 2100, 2700, 2910, 3070, 3110, 3600, 3970,
4080, 4350, 4370, 4680, 5020, 5450, 5740, 6140 ]
default: 2910
motorcomm,rx-data-drv-microamp:
description: |
drive strength of rx_data/rx_ctl rgmii pad.
The YT8531 RGMII LDO voltage supports 1.8V/3.3V, and the LDO voltage can
be configured with hardware pull-up resistors to match the SOC voltage
(usually 1.8V).
The software can read the registers to obtain the LDO voltage and configure
the legal drive strength(curren).
=====================================================
| voltage | current Available (uA) |
| 1.8v | 1200 2100 2700 2910 3110 3600 3970 4350 |
| 3.3v | 3070 4080 4370 4680 5020 5450 5740 6140 |
=====================================================
enum: [ 1200, 2100, 2700, 2910, 3070, 3110, 3600, 3970,
4080, 4350, 4370, 4680, 5020, 5450, 5740, 6140 ]
default: 2910
motorcomm,tx-clk-adj-enabled:
description: |
This configuration is mainly to adapt to VF2 with JH7110 SoC.

View File

@ -1,41 +0,0 @@
* Oxford Semiconductor OXNAS DWMAC Ethernet controller
The device inherits all the properties of the dwmac/stmmac devices
described in the file stmmac.txt in the current directory with the
following changes.
Required properties on all platforms:
- compatible: For the OX820 SoC, it should be :
- "oxsemi,ox820-dwmac" to select glue
- "snps,dwmac-3.512" to select IP version.
For the OX810SE SoC, it should be :
- "oxsemi,ox810se-dwmac" to select glue
- "snps,dwmac-3.512" to select IP version.
- clocks: Should contain phandles to the following clocks
- clock-names: Should contain the following:
- "stmmaceth" for the host clock - see stmmac.txt
- "gmac" for the peripheral gate clock
- oxsemi,sys-ctrl: a phandle to the system controller syscon node
Example :
etha: ethernet@40400000 {
compatible = "oxsemi,ox820-dwmac", "snps,dwmac-3.512";
reg = <0x40400000 0x2000>;
interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq";
mac-address = [000000000000]; /* Filled in by U-Boot */
phy-mode = "rgmii";
clocks = <&stdclk CLK_820_ETHA>, <&gmacclk>;
clock-names = "gmac", "stmmaceth";
resets = <&reset RESET_MAC>;
/* Regmap for sys registers */
oxsemi,sys-ctrl = <&sys>;
};

View File

@ -75,6 +75,7 @@ properties:
description:
Initial data for the VDDIO regulator. Set this to 1.5V or 1.8V.
$ref: /schemas/regulator/regulator.yaml
unevaluatedProperties: false
vddh-regulator:
type: object
@ -82,6 +83,7 @@ properties:
Dummy subnode to model the external connection of the PHY VDDH
regulator to VDDIO.
$ref: /schemas/regulator/regulator.yaml
unevaluatedProperties: false
unevaluatedProperties: false

View File

@ -80,6 +80,7 @@ properties:
"output" means GMAC provides the reference clock.
$ref: /schemas/types.yaml#/definitions/string
enum: [input, output]
default: input
rockchip,grf:
description: The phandle of the syscon node for the general register file.

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/ti,icss-iep.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments ICSS Industrial Ethernet Peripheral (IEP) module
maintainers:
- Md Danish Anwar <danishanwar@ti.com>
properties:
compatible:
oneOf:
- items:
- enum:
- ti,am642-icss-iep
- ti,j721e-icss-iep
- const: ti,am654-icss-iep
- const: ti,am654-icss-iep
reg:
maxItems: 1
clocks:
maxItems: 1
description: phandle to the IEP source clock
required:
- compatible
- reg
- clocks
additionalProperties: false
examples:
- |
/* AM65x */
icssg0_iep0: iep@2e000 {
compatible = "ti,am654-icss-iep";
reg = <0x2e000 0x1000>;
clocks = <&icssg0_iepclk_mux>;
};

View File

@ -0,0 +1,193 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/ti,icssg-prueth.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments ICSSG PRUSS Ethernet
maintainers:
- Md Danish Anwar <danishanwar@ti.com>
description:
Ethernet based on the Programmable Real-Time Unit and Industrial
Communication Subsystem.
allOf:
- $ref: /schemas/remoteproc/ti,pru-consumer.yaml#
properties:
compatible:
enum:
- ti,am654-icssg-prueth # for AM65x SoC family
sram:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to MSMC SRAM node
dmas:
maxItems: 10
dma-names:
items:
- const: tx0-0
- const: tx0-1
- const: tx0-2
- const: tx0-3
- const: tx1-0
- const: tx1-1
- const: tx1-2
- const: tx1-3
- const: rx0
- const: rx1
ti,mii-g-rt:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to MII_G_RT module's syscon regmap.
ti,mii-rt:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to MII_RT module's syscon regmap
ti,iep:
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 2
items:
maxItems: 1
description:
phandle to IEP (Industrial Ethernet Peripheral) for ICSSG
interrupts:
maxItems: 2
description:
Interrupt specifiers to TX timestamp IRQ.
interrupt-names:
items:
- const: tx_ts0
- const: tx_ts1
ethernet-ports:
type: object
additionalProperties: false
properties:
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
^port@[0-1]$:
type: object
description: ICSSG PRUETH external ports
$ref: ethernet-controller.yaml#
unevaluatedProperties: false
properties:
reg:
items:
- enum: [0, 1]
description: ICSSG PRUETH port number
interrupts:
maxItems: 1
ti,syscon-rgmii-delay:
items:
- items:
- description: phandle to system controller node
- description: The offset to ICSSG control register
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
phandle to system controller node and register offset
to ICSSG control register for RGMII transmit delay
required:
- reg
anyOf:
- required:
- port@0
- required:
- port@1
required:
- compatible
- sram
- dmas
- dma-names
- ethernet-ports
- ti,mii-g-rt
- interrupts
- interrupt-names
unevaluatedProperties: false
examples:
- |
/* Example k3-am654 base board SR2.0, dual-emac */
pruss2_eth: ethernet {
compatible = "ti,am654-icssg-prueth";
pinctrl-names = "default";
pinctrl-0 = <&icssg2_rgmii_pins_default>;
sram = <&msmc_ram>;
ti,prus = <&pru2_0>, <&rtu2_0>, <&tx_pru2_0>,
<&pru2_1>, <&rtu2_1>, <&tx_pru2_1>;
firmware-name = "ti-pruss/am65x-pru0-prueth-fw.elf",
"ti-pruss/am65x-rtu0-prueth-fw.elf",
"ti-pruss/am65x-txpru0-prueth-fw.elf",
"ti-pruss/am65x-pru1-prueth-fw.elf",
"ti-pruss/am65x-rtu1-prueth-fw.elf",
"ti-pruss/am65x-txpru1-prueth-fw.elf";
ti,pruss-gp-mux-sel = <2>, /* MII mode */
<2>,
<2>,
<2>, /* MII mode */
<2>,
<2>;
dmas = <&main_udmap 0xc300>, /* egress slice 0 */
<&main_udmap 0xc301>, /* egress slice 0 */
<&main_udmap 0xc302>, /* egress slice 0 */
<&main_udmap 0xc303>, /* egress slice 0 */
<&main_udmap 0xc304>, /* egress slice 1 */
<&main_udmap 0xc305>, /* egress slice 1 */
<&main_udmap 0xc306>, /* egress slice 1 */
<&main_udmap 0xc307>, /* egress slice 1 */
<&main_udmap 0x4300>, /* ingress slice 0 */
<&main_udmap 0x4301>; /* ingress slice 1 */
dma-names = "tx0-0", "tx0-1", "tx0-2", "tx0-3",
"tx1-0", "tx1-1", "tx1-2", "tx1-3",
"rx0", "rx1";
ti,mii-g-rt = <&icssg2_mii_g_rt>;
ti,iep = <&icssg2_iep0>, <&icssg2_iep1>;
interrupt-parent = <&icssg2_intc>;
interrupts = <24 0 2>, <25 1 3>;
interrupt-names = "tx_ts0", "tx_ts1";
ethernet-ports {
#address-cells = <1>;
#size-cells = <0>;
pruss2_emac0: port@0 {
reg = <0>;
phy-handle = <&pruss2_eth0_phy>;
phy-mode = "rgmii-id";
interrupts-extended = <&icssg2_intc 24>;
ti,syscon-rgmii-delay = <&scm_conf 0x4120>;
/* Filled in by bootloader */
local-mac-address = [00 00 00 00 00 00];
};
pruss2_emac1: port@1 {
reg = <1>;
phy-handle = <&pruss2_eth1_phy>;
phy-mode = "rgmii-id";
interrupts-extended = <&icssg2_intc 25>;
ti,syscon-rgmii-delay = <&scm_conf 0x4124>;
/* Filled in by bootloader */
local-mac-address = [00 00 00 00 00 00];
};
};
};

View File

@ -28,6 +28,7 @@ properties:
- mediatek,mt76
- mediatek,mt7628-wmac
- mediatek,mt7622-wmac
- mediatek,mt7981-wmac
- mediatek,mt7986-wmac
reg:
@ -71,6 +72,14 @@ properties:
ieee80211-freq-limit: true
nvmem-cells:
items:
- description: NVMEM cell with EEPROM
nvmem-cell-names:
items:
- const: eeprom
mediatek,eeprom-data:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
@ -84,6 +93,7 @@ properties:
- description: offset containing EEPROM data
description:
Phandle to a MTD partition + offset containing EEPROM data
deprecated: true
big-endian:
$ref: /schemas/types.yaml#/definitions/flag
@ -258,7 +268,8 @@ examples:
interrupt-parent = <&cpuintc>;
interrupts = <6>;
mediatek,mtd-eeprom = <&factory 0x0>;
nvmem-cells = <&eeprom>;
nvmem-cell-names = "eeprom";
};
- |

View File

@ -1,35 +0,0 @@
XILINX GMIITORGMII Converter Driver Device Tree Bindings
--------------------------------------------------------
The Gigabit Media Independent Interface (GMII) to Reduced Gigabit Media
Independent Interface (RGMII) core provides the RGMII between RGMII-compliant
Ethernet physical media devices (PHY) and the Gigabit Ethernet controller.
This core can be used in all three modes of operation(10/100/1000 Mb/s).
The Management Data Input/Output (MDIO) interface is used to configure the
Speed of operation. This core can switch dynamically between the three
Different speed modes by configuring the conveter register through mdio write.
This converter sits between the ethernet MAC and the external phy.
MAC <==> GMII2RGMII <==> RGMII_PHY
For more details about mdio please refer phy.txt file in the same directory.
Required properties:
- compatible : Should be "xlnx,gmii-to-rgmii-1.0"
- reg : The ID number for the phy, usually a small integer
- phy-handle : Should point to the external phy device.
See ethernet.txt file in the same directory.
Example:
mdio {
#address-cells = <1>;
#size-cells = <0>;
phy: ethernet-phy@0 {
......
};
gmiitorgmii: gmiitorgmii@8 {
compatible = "xlnx,gmii-to-rgmii-1.0";
reg = <8>;
phy-handle = <&phy>;
};
};

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/xlnx,gmii-to-rgmii.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx GMII to RGMII Converter
maintainers:
- Harini Katakam <harini.katakam@amd.com>
description:
The Gigabit Media Independent Interface (GMII) to Reduced Gigabit Media
Independent Interface (RGMII) core provides the RGMII between RGMII-compliant
ethernet physical media devices (PHY) and the Gigabit Ethernet controller.
This core can be used in all three modes of operation(10/100/1000 Mb/s).
The Management Data Input/Output (MDIO) interface is used to configure the
speed of operation. This core can switch dynamically between the three
different speed modes by configuring the converter register through mdio write.
The core cannot function without an external phy connected to it.
properties:
compatible:
const: xlnx,gmii-to-rgmii-1.0
reg:
minimum: 0
maximum: 31
description: The ID number for the phy.
phy-handle:
$ref: ethernet-controller.yaml#/properties/phy-handle
required:
- compatible
- reg
- phy-handle
unevaluatedProperties: false
examples:
- |
mdio {
#address-cells = <1>;
#size-cells = <0>;
phy: ethernet-phy@0 {
reg = <0>;
};
gmiitorgmii@8 {
compatible = "xlnx,gmii-to-rgmii-1.0";
reg = <8>;
phy-handle = <&phy>;
};
};

View File

@ -41,7 +41,7 @@ properties:
description: Name of the define for the family name.
type: string
c-version-name:
description: Name of the define for the verion of the family.
description: Name of the define for the version of the family.
type: string
max-by-define:
description: Makes the number of attributes and commands be specified by a define, not an enum value.
@ -274,7 +274,7 @@ properties:
description: Kernel attribute validation flags.
type: array
items:
enum: [ strict, dump ]
enum: [ strict, dump, dump-strict ]
do: &subop-type
description: Main command handler.
type: object

View File

@ -41,7 +41,7 @@ properties:
description: Name of the define for the family name.
type: string
c-version-name:
description: Name of the define for the verion of the family.
description: Name of the define for the version of the family.
type: string
max-by-define:
description: Makes the number of attributes and commands be specified by a define, not an enum value.
@ -321,7 +321,7 @@ properties:
description: Kernel attribute validation flags.
type: array
items:
enum: [ strict, dump ]
enum: [ strict, dump, dump-strict ]
# Start genetlink-legacy
fixed-header: *fixed-header
# End genetlink-legacy

View File

@ -243,7 +243,7 @@ properties:
description: Kernel attribute validation flags.
type: array
items:
enum: [ strict, dump ]
enum: [ strict, dump, dump-strict ]
do: &subop-type
description: Main command handler.
type: object

View File

@ -0,0 +1,410 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
%YAML 1.2
---
$id: http://kernel.org/schemas/netlink/netlink-raw.yaml#
$schema: https://json-schema.org/draft-07/schema
# Common defines
$defs:
uint:
type: integer
minimum: 0
len-or-define:
type: [ string, integer ]
pattern: ^[0-9A-Za-z_]+( - 1)?$
minimum: 0
# Schema for specs
title: Protocol
description: Specification of a raw netlink protocol
type: object
required: [ name, doc, attribute-sets, operations ]
additionalProperties: False
properties:
name:
description: Name of the netlink family.
type: string
doc:
type: string
protocol:
description: Schema compatibility level.
enum: [ netlink-raw ] # Trim
# Start netlink-raw
protonum:
description: Protocol number to use for netlink-raw
type: integer
# End netlink-raw
uapi-header:
description: Path to the uAPI header, default is linux/${family-name}.h
type: string
# Start genetlink-c
c-family-name:
description: Name of the define for the family name.
type: string
c-version-name:
description: Name of the define for the version of the family.
type: string
max-by-define:
description: Makes the number of attributes and commands be specified by a define, not an enum value.
type: boolean
# End genetlink-c
# Start genetlink-legacy
kernel-policy:
description: |
Defines if the input policy in the kernel is global, per-operation, or split per operation type.
Default is split.
enum: [ split, per-op, global ]
# End genetlink-legacy
definitions:
description: List of type and constant definitions (enums, flags, defines).
type: array
items:
type: object
required: [ type, name ]
additionalProperties: False
properties:
name:
type: string
header:
description: For C-compatible languages, header which already defines this value.
type: string
type:
enum: [ const, enum, flags, struct ] # Trim
doc:
type: string
# For const
value:
description: For const - the value.
type: [ string, integer ]
# For enum and flags
value-start:
description: For enum or flags the literal initializer for the first value.
type: [ string, integer ]
entries:
description: For enum or flags array of values.
type: array
items:
oneOf:
- type: string
- type: object
required: [ name ]
additionalProperties: False
properties:
name:
type: string
value:
type: integer
doc:
type: string
render-max:
description: Render the max members for this enum.
type: boolean
# Start genetlink-c
enum-name:
description: Name for enum, if empty no name will be used.
type: [ string, "null" ]
name-prefix:
description: For enum the prefix of the values, optional.
type: string
# End genetlink-c
# Start genetlink-legacy
members:
description: List of struct members. Only scalars and strings members allowed.
type: array
items:
type: object
required: [ name, type ]
additionalProperties: False
properties:
name:
type: string
type:
description: The netlink attribute type
enum: [ u8, u16, u32, u64, s8, s16, s32, s64, string, binary ]
len:
$ref: '#/$defs/len-or-define'
byte-order:
enum: [ little-endian, big-endian ]
doc:
description: Documentation for the struct member attribute.
type: string
enum:
description: Name of the enum type used for the attribute.
type: string
enum-as-flags:
description: |
Treat the enum as flags. In most cases enum is either used as flags or as values.
Sometimes, however, both forms are necessary, in which case header contains the enum
form while specific attributes may request to convert the values into a bitfield.
type: boolean
display-hint: &display-hint
description: |
Optional format indicator that is intended only for choosing
the right formatting mechanism when displaying values of this
type.
enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
# End genetlink-legacy
attribute-sets:
description: Definition of attribute spaces for this family.
type: array
items:
description: Definition of a single attribute space.
type: object
required: [ name, attributes ]
additionalProperties: False
properties:
name:
description: |
Name used when referring to this space in other definitions, not used outside of the spec.
type: string
name-prefix:
description: |
Prefix for the C enum name of the attributes. Default family[name]-set[name]-a-
type: string
enum-name:
description: Name for the enum type of the attribute.
type: string
doc:
description: Documentation of the space.
type: string
subset-of:
description: |
Name of another space which this is a logical part of. Sub-spaces can be used to define
a limited group of attributes which are used in a nest.
type: string
# Start genetlink-c
attr-cnt-name:
description: The explicit name for constant holding the count of attributes (last attr + 1).
type: string
attr-max-name:
description: The explicit name for last member of attribute enum.
type: string
# End genetlink-c
attributes:
description: List of attributes in the space.
type: array
items:
type: object
required: [ name, type ]
additionalProperties: False
properties:
name:
type: string
type: &attr-type
description: The netlink attribute type
enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64,
string, nest, array-nest, nest-type-value ]
doc:
description: Documentation of the attribute.
type: string
value:
description: Value for the enum item representing this attribute in the uAPI.
$ref: '#/$defs/uint'
type-value:
description: Name of the value extracted from the type of a nest-type-value attribute.
type: array
items:
type: string
byte-order:
enum: [ little-endian, big-endian ]
multi-attr:
type: boolean
nested-attributes:
description: Name of the space (sub-space) used inside the attribute.
type: string
enum:
description: Name of the enum type used for the attribute.
type: string
enum-as-flags:
description: |
Treat the enum as flags. In most cases enum is either used as flags or as values.
Sometimes, however, both forms are necessary, in which case header contains the enum
form while specific attributes may request to convert the values into a bitfield.
type: boolean
checks:
description: Kernel input validation.
type: object
additionalProperties: False
properties:
flags-mask:
description: Name of the flags constant on which to base mask (unsigned scalar types only).
type: string
min:
description: Min value for an integer attribute.
type: integer
min-len:
description: Min length for a binary attribute.
$ref: '#/$defs/len-or-define'
max-len:
description: Max length for a string or a binary attribute.
$ref: '#/$defs/len-or-define'
sub-type: *attr-type
display-hint: *display-hint
# Start genetlink-c
name-prefix:
type: string
# End genetlink-c
# Start genetlink-legacy
struct:
description: Name of the struct type used for the attribute.
type: string
# End genetlink-legacy
# Make sure name-prefix does not appear in subsets (subsets inherit naming)
dependencies:
name-prefix:
not:
required: [ subset-of ]
subset-of:
not:
required: [ name-prefix ]
operations:
description: Operations supported by the protocol.
type: object
required: [ list ]
additionalProperties: False
properties:
enum-model:
description: |
The model of assigning values to the operations.
"unified" is the recommended model where all message types belong
to a single enum.
"directional" has the messages sent to the kernel and from the kernel
enumerated separately.
enum: [ unified, directional ] # Trim
name-prefix:
description: |
Prefix for the C enum name of the command. The name is formed by concatenating
the prefix with the upper case name of the command, with dashes replaced by underscores.
type: string
enum-name:
description: Name for the enum type with commands.
type: string
async-prefix:
description: Same as name-prefix but used to render notifications and events to separate enum.
type: string
async-enum:
description: Name for the enum type with notifications/events.
type: string
# Start genetlink-legacy
fixed-header: &fixed-header
description: |
Name of the structure defining the optional fixed-length protocol
header. This header is placed in a message after the netlink and
genetlink headers and before any attributes.
type: string
# End genetlink-legacy
list:
description: List of commands
type: array
items:
type: object
additionalProperties: False
required: [ name, doc ]
properties:
name:
description: Name of the operation, also defining its C enum value in uAPI.
type: string
doc:
description: Documentation for the command.
type: string
value:
description: Value for the enum in the uAPI.
$ref: '#/$defs/uint'
attribute-set:
description: |
Attribute space from which attributes directly in the requests and replies
to this command are defined.
type: string
flags: &cmd_flags
description: Command flags.
type: array
items:
enum: [ admin-perm ]
dont-validate:
description: Kernel attribute validation flags.
type: array
items:
enum: [ strict, dump ]
# Start genetlink-legacy
fixed-header: *fixed-header
# End genetlink-legacy
do: &subop-type
description: Main command handler.
type: object
additionalProperties: False
properties:
request: &subop-attr-list
description: Definition of the request message for a given command.
type: object
additionalProperties: False
properties:
attributes:
description: |
Names of attributes from the attribute-set (not full attribute
definitions, just names).
type: array
items:
type: string
# Start genetlink-legacy
value:
description: |
ID of this message if value for request and response differ,
i.e. requests and responses have different message enums.
$ref: '#/$defs/uint'
# End genetlink-legacy
reply: *subop-attr-list
pre:
description: Hook for a function to run before the main callback (pre_doit or start).
type: string
post:
description: Hook for a function to run after the main callback (post_doit or done).
type: string
dump: *subop-type
notify:
description: Name of the command sharing the reply type with this notification.
type: string
event:
type: object
additionalProperties: False
properties:
attributes:
description: Explicit list of the attributes for the notification.
type: array
items:
type: string
mcgrp:
description: Name of the multicast group generating given notification.
type: string
mcast-groups:
description: List of multicast groups.
type: object
required: [ list ]
additionalProperties: False
properties:
list:
description: List of groups.
type: array
items:
type: object
required: [ name ]
additionalProperties: False
properties:
name:
description: |
The name for the group, used to form the define and the value of the define.
type: string
# Start genetlink-c
c-define-name:
description: Override for the name of the define in C uAPI.
type: string
# End genetlink-c
flags: *cmd_flags
# Start netlink-raw
value:
description: Value of the netlink multicast group in the uAPI.
type: integer
# End netlink-raw

View File

@ -6,6 +6,16 @@ protocol: genetlink-legacy
doc: Partial family for Devlink.
definitions:
-
type: enum
name: sb-pool-type
entries:
-
name: ingress
-
name: egress
attribute-sets:
-
name: devlink
@ -24,6 +34,46 @@ attribute-sets:
# TODO: fill in the attributes in between
-
name: sb-index
type: u32
value: 11
# TODO: fill in the attributes in between
-
name: sb-pool-index
type: u16
value: 17
-
name: sb-pool-type
type: u8
enum: sb-pool-type
# TODO: fill in the attributes in between
-
name: sb-tc-index
type: u16
value: 22
# TODO: fill in the attributes in between
-
name: param-name
type: string
value: 81
# TODO: fill in the attributes in between
-
name: region-name
type: string
value: 88
# TODO: fill in the attributes in between
-
name: info-driver-name
type: string
@ -55,10 +105,35 @@ attribute-sets:
# TODO: fill in the attributes in between
-
name: health-reporter-name
type: string
value: 115
# TODO: fill in the attributes in between
-
name: trap-name
type: string
value: 130
# TODO: fill in the attributes in between
-
name: trap-group-name
type: string
value: 135
-
name: reload-failed
type: u8
value: 136
# TODO: fill in the attributes in between
-
name: trap-policer-id
type: u32
value: 142
# TODO: fill in the attributes in between
@ -103,6 +178,21 @@ attribute-sets:
type: nest
multi-attr: true
nested-attributes: dl-reload-act-stats
# TODO: fill in the attributes in between
-
name: rate-node-name
type: string
value: 168
# TODO: fill in the attributes in between
-
name: linecard-index
type: u32
value: 171
-
name: dl-dev-stats
subset-of: devlink
@ -165,8 +255,13 @@ operations:
name: get
doc: Get devlink instances.
attribute-set: devlink
dont-validate:
- strict
- dump
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 1
attributes: &dev-id-attrs
@ -183,18 +278,212 @@ operations:
dump:
reply: *get-reply
-
name: port-get
doc: Get devlink port instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit-port
post: devlink-nl-post-doit
request:
value: 5
attributes: &port-id-attrs
- bus-name
- dev-name
- port-index
reply:
value: 7
attributes: *port-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply:
value: 3 # due to a bug, port dump returns DEVLINK_CMD_NEW
attributes: *port-id-attrs
# TODO: fill in the operations in between
-
name: sb-get
doc: Get shared buffer instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 11
attributes: &sb-id-attrs
- bus-name
- dev-name
- sb-index
reply: &sb-get-reply
value: 11
attributes: *sb-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *sb-get-reply
# TODO: fill in the operations in between
-
name: sb-pool-get
doc: Get shared buffer pool instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 15
attributes: &sb-pool-id-attrs
- bus-name
- dev-name
- sb-index
- sb-pool-index
reply: &sb-pool-get-reply
value: 15
attributes: *sb-pool-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *sb-pool-get-reply
# TODO: fill in the operations in between
-
name: sb-port-pool-get
doc: Get shared buffer port-pool combinations and threshold.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit-port
post: devlink-nl-post-doit
request:
value: 19
attributes: &sb-port-pool-id-attrs
- bus-name
- dev-name
- port-index
- sb-index
- sb-pool-index
reply: &sb-port-pool-get-reply
value: 19
attributes: *sb-port-pool-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *sb-port-pool-get-reply
# TODO: fill in the operations in between
-
name: sb-tc-pool-bind-get
doc: Get shared buffer port-TC to pool bindings and threshold.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit-port
post: devlink-nl-post-doit
request:
value: 23
attributes: &sb-tc-pool-bind-id-attrs
- bus-name
- dev-name
- port-index
- sb-index
- sb-pool-type
- sb-tc-index
reply: &sb-tc-pool-bind-get-reply
value: 23
attributes: *sb-tc-pool-bind-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *sb-tc-pool-bind-get-reply
# TODO: fill in the operations in between
-
name: param-get
doc: Get param instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 38
attributes: &param-id-attrs
- bus-name
- dev-name
- param-name
reply: &param-get-reply
value: 38
attributes: *param-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *param-get-reply
# TODO: fill in the operations in between
-
name: region-get
doc: Get region instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit-port-optional
post: devlink-nl-post-doit
request:
value: 42
attributes: &region-id-attrs
- bus-name
- dev-name
- port-index
- region-name
reply: &region-get-reply
value: 42
attributes: *region-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *region-get-reply
# TODO: fill in the operations in between
-
name: info-get
doc: Get device information, like driver name, hardware and firmware versions etc.
attribute-set: devlink
dont-validate:
- strict
- dump
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 51
attributes: *dev-id-attrs
reply:
reply: &info-get-reply
value: 51
attributes:
- bus-name
@ -204,3 +493,181 @@ operations:
- info-version-fixed
- info-version-running
- info-version-stored
dump:
reply: *info-get-reply
-
name: health-reporter-get
doc: Get health reporter instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit-port-optional
post: devlink-nl-post-doit
request:
attributes: &health-reporter-id-attrs
- bus-name
- dev-name
- port-index
- health-reporter-name
reply: &health-reporter-get-reply
attributes: *health-reporter-id-attrs
dump:
request:
attributes: *port-id-attrs
reply: *health-reporter-get-reply
# TODO: fill in the operations in between
-
name: trap-get
doc: Get trap instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 61
attributes: &trap-id-attrs
- bus-name
- dev-name
- trap-name
reply: &trap-get-reply
value: 61
attributes: *trap-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *trap-get-reply
# TODO: fill in the operations in between
-
name: trap-group-get
doc: Get trap group instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 65
attributes: &trap-group-id-attrs
- bus-name
- dev-name
- trap-group-name
reply: &trap-group-get-reply
value: 65
attributes: *trap-group-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *trap-group-get-reply
# TODO: fill in the operations in between
-
name: trap-policer-get
doc: Get trap policer instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 69
attributes: &trap-policer-id-attrs
- bus-name
- dev-name
- trap-policer-id
reply: &trap-policer-get-reply
value: 69
attributes: *trap-policer-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *trap-policer-get-reply
# TODO: fill in the operations in between
-
name: rate-get
doc: Get rate instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 74
attributes: &rate-id-attrs
- bus-name
- dev-name
- port-index
- rate-node-name
reply: &rate-get-reply
value: 74
attributes: *rate-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *rate-get-reply
# TODO: fill in the operations in between
-
name: linecard-get
doc: Get line card instances.
attribute-set: devlink
dont-validate:
- strict
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 78
attributes: &linecard-id-attrs
- bus-name
- dev-name
- linecard-index
reply: &linecard-get-reply
value: 78
attributes: *linecard-id-attrs
dump:
request:
attributes: *dev-id-attrs
reply: *linecard-get-reply
# TODO: fill in the operations in between
-
name: selftests-get
doc: Get device selftest instances.
attribute-set: devlink
dont-validate:
- strict
- dump
do:
pre: devlink-nl-pre-doit
post: devlink-nl-post-doit
request:
value: 82
attributes: *dev-id-attrs
reply: &selftests-get-reply
value: 82
attributes: *dev-id-attrs
dump:
reply: *selftests-get-reply

View File

@ -107,16 +107,16 @@ operations:
flags: [ admin-perm ]
do:
request: &select_attrs
request: &select_attrs
attributes:
- af
- ifindex
- port
- peer_port
- local_v4
- peer_v4
- local_v6
- peer_v6
- af
- ifindex
- port
- peer_port
- local_v4
- peer_v4
- local_v6
- peer_v6
-
name: get

View File

@ -14,7 +14,7 @@ definitions:
-
name: basic
doc:
XDP feautues set supported by all drivers
XDP features set supported by all drivers
(XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX)
-
name: redirect
@ -62,6 +62,12 @@ attribute-sets:
type: u64
enum: xdp-act
enum-as-flags: true
-
name: xdp-zc-max-segs
doc: max fragment count supported by ZC driver
type: u32
checks:
min: 1
operations:
list:
@ -77,6 +83,7 @@ operations:
attributes:
- ifindex
- xdp-features
- xdp-zc-max-segs
dump:
reply: *dev-all
-

View File

@ -81,6 +81,10 @@ attribute-sets:
name-prefix: ovs-vport-attr-
enum-name: ovs-vport-attr
attributes:
-
name: unspec
type: unused
value: 0
-
name: port-no
type: u32
@ -120,10 +124,35 @@ attribute-sets:
operations:
name-prefix: ovs-vport-cmd-
list:
-
name: new
doc: Create a new OVS vport
attribute-set: vport
fixed-header: ovs-header
do:
request:
attributes:
- name
- type
- upcall-pid
- dp-ifindex
- ifindex
- options
-
name: del
doc: Delete existing OVS vport from a data path
attribute-set: vport
fixed-header: ovs-header
do:
request:
attributes:
- dp-ifindex
- port-no
- type
- name
-
name: get
doc: Get / dump OVS vport configuration and state
value: 3
attribute-set: vport
fixed-header: ovs-header
do: &vport-get-op

View File

@ -0,0 +1,179 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: rt-addr
protocol: netlink-raw
protonum: 0
doc:
Address configuration over rtnetlink.
definitions:
-
name: ifaddrmsg
type: struct
members:
-
name: ifa-family
type: u8
-
name: ifa-prefixlen
type: u8
-
name: ifa-flags
type: u8
enum: ifa-flags
enum-as-flags: true
-
name: ifa-scope
type: u8
-
name: ifa-index
type: u32
-
name: ifa-cacheinfo
type: struct
members:
-
name: ifa-prefered
type: u32
-
name: ifa-valid
type: u32
-
name: cstamp
type: u32
-
name: tstamp
type: u32
-
name: ifa-flags
type: flags
entries:
-
name: secondary
-
name: nodad
-
name: optimistic
-
name: dadfailed
-
name: homeaddress
-
name: deprecated
-
name: tentative
-
name: permanent
-
name: managetempaddr
-
name: noprefixroute
-
name: mcautojoin
-
name: stable-privacy
attribute-sets:
-
name: addr-attrs
attributes:
-
name: ifa-address
type: binary
display-hint: ipv4
-
name: ifa-local
type: binary
display-hint: ipv4
-
name: ifa-label
type: string
-
name: ifa-broadcast
type: binary
display-hint: ipv4
-
name: ifa-anycast
type: binary
-
name: ifa-cacheinfo
type: binary
struct: ifa-cacheinfo
-
name: ifa-multicast
type: binary
-
name: ifa-flags
type: u32
enum: ifa-flags
enum-as-flags: true
-
name: ifa-rt-priority
type: u32
-
name: ifa-target-netnsid
type: binary
-
name: ifa-proto
type: u8
operations:
fixed-header: ifaddrmsg
enum-model: directional
list:
-
name: newaddr
doc: Add new address
attribute-set: addr-attrs
do:
request:
value: 20
attributes: &ifaddr-all
- ifa-family
- ifa-flags
- ifa-prefixlen
- ifa-scope
- ifa-index
- ifa-address
- ifa-label
- ifa-local
- ifa-cacheinfo
-
name: deladdr
doc: Remove address
attribute-set: addr-attrs
do:
request:
value: 21
attributes:
- ifa-family
- ifa-flags
- ifa-prefixlen
- ifa-scope
- ifa-index
- ifa-address
- ifa-local
-
name: getaddr
doc: Dump address information.
attribute-set: addr-attrs
dump:
request:
value: 22
attributes:
- ifa-index
reply:
value: 20
attributes: *ifaddr-all
mcast-groups:
list:
-
name: rtnlgrp-ipv4-ifaddr
value: 5
-
name: rtnlgrp-ipv6-ifaddr
value: 9

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,327 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: rt-route
protocol: netlink-raw
protonum: 0
doc:
Route configuration over rtnetlink.
definitions:
-
name: rtm-type
name-prefix: rtn-
type: enum
entries:
- unspec
- unicast
- local
- broadcast
- anycast
- multicast
- blackhole
- unreachable
- prohibit
- throw
- nat
- xresolve
-
name: rtmsg
type: struct
members:
-
name: rtm-family
type: u8
-
name: rtm-dst-len
type: u8
-
name: rtm-src-len
type: u8
-
name: rtm-tos
type: u8
-
name: rtm-table
type: u8
-
name: rtm-protocol
type: u8
-
name: rtm-scope
type: u8
-
name: rtm-type
type: u8
enum: rtm-type
-
name: rtm-flags
type: u32
-
name: rta-cacheinfo
type: struct
members:
-
name: rta-clntref
type: u32
-
name: rta-lastuse
type: u32
-
name: rta-expires
type: u32
-
name: rta-error
type: u32
-
name: rta-used
type: u32
attribute-sets:
-
name: route-attrs
attributes:
-
name: rta-dst
type: binary
display-hint: ipv4
-
name: rta-src
type: binary
display-hint: ipv4
-
name: rta-iif
type: u32
-
name: rta-oif
type: u32
-
name: rta-gateway
type: binary
display-hint: ipv4
-
name: rta-priority
type: u32
-
name: rta-prefsrc
type: binary
display-hint: ipv4
-
name: rta-metrics
type: nest
nested-attributes: rta-metrics
-
name: rta-multipath
type: binary
-
name: rta-protoinfo # not used
type: binary
-
name: rta-flow
type: u32
-
name: rta-cacheinfo
type: binary
struct: rta-cacheinfo
-
name: rta-session # not used
type: binary
-
name: rta-mp-algo # not used
type: binary
-
name: rta-table
type: u32
-
name: rta-mark
type: u32
-
name: rta-mfc-stats
type: binary
-
name: rta-via
type: binary
-
name: rta-newdst
type: binary
-
name: rta-pref
type: u8
-
name: rta-encap-type
type: u16
-
name: rta-encap
type: binary # tunnel specific nest
-
name: rta-expires
type: u32
-
name: rta-pad
type: binary
-
name: rta-uid
type: u32
-
name: rta-ttl-propagate
type: u8
-
name: rta-ip-proto
type: u8
-
name: rta-sport
type: u16
-
name: rta-dport
type: u16
-
name: rta-nh-id
type: u32
-
name: rta-metrics
attributes:
-
name: rtax-unspec
type: unused
value: 0
-
name: rtax-lock
type: u32
-
name: rtax-mtu
type: u32
-
name: rtax-window
type: u32
-
name: rtax-rtt
type: u32
-
name: rtax-rttvar
type: u32
-
name: rtax-ssthresh
type: u32
-
name: rtax-cwnd
type: u32
-
name: rtax-advmss
type: u32
-
name: rtax-reordering
type: u32
-
name: rtax-hoplimit
type: u32
-
name: rtax-initcwnd
type: u32
-
name: rtax-features
type: u32
-
name: rtax-rto-min
type: u32
-
name: rtax-initrwnd
type: u32
-
name: rtax-quickack
type: u32
-
name: rtax-cc-algo
type: string
-
name: rtax-fastopen-no-cookie
type: u32
operations:
enum-model: directional
list:
-
name: getroute
doc: Dump route information.
attribute-set: route-attrs
fixed-header: rtmsg
do:
request:
value: 26
attributes:
- rtm-family
- rta-src
- rtm-src-len
- rta-dst
- rtm-dst-len
- rta-iif
- rta-oif
- rta-ip-proto
- rta-sport
- rta-dport
- rta-mark
- rta-uid
reply:
value: 24
attributes: &all-route-attrs
- rtm-family
- rtm-dst-len
- rtm-src-len
- rtm-tos
- rtm-table
- rtm-protocol
- rtm-scope
- rtm-type
- rtm-flags
- rta-dst
- rta-src
- rta-iif
- rta-oif
- rta-gateway
- rta-priority
- rta-prefsrc
- rta-metrics
- rta-multipath
- rta-flow
- rta-cacheinfo
- rta-table
- rta-mark
- rta-mfc-stats
- rta-via
- rta-newdst
- rta-pref
- rta-encap-type
- rta-encap
- rta-expires
- rta-pad
- rta-uid
- rta-ttl-propagate
- rta-ip-proto
- rta-sport
- rta-dport
- rta-nh-id
dump:
request:
value: 26
attributes:
- rtm-family
reply:
value: 24
attributes: *all-route-attrs
-
name: newroute
doc: Create a new route
attribute-set: route-attrs
fixed-header: rtmsg
do:
request:
value: 24
attributes: *all-route-attrs
-
name: delroute
doc: Delete an existing route
attribute-set: route-attrs
fixed-header: rtmsg
do:
request:
value: 25
attributes: *all-route-attrs

View File

@ -462,8 +462,92 @@ 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.
Multi-Buffer Support
====================
With multi-buffer support, programs using AF_XDP sockets can receive
and transmit packets consisting of multiple buffers both in copy and
zero-copy mode. For example, a packet can consist of two
frames/buffers, one with the header and the other one with the data,
or a 9K Ethernet jumbo frame can be constructed by chaining together
three 4K frames.
Some definitions:
* A packet consists of one or more frames
* A descriptor in one of the AF_XDP rings always refers to a single
frame. In the case the packet consists of a single frame, the
descriptor refers to the whole packet.
To enable multi-buffer support for an AF_XDP socket, use the new bind
flag XDP_USE_SG. If this is not provided, all multi-buffer packets
will be dropped just as before. Note that the XDP program loaded also
needs to be in multi-buffer mode. This can be accomplished by using
"xdp.frags" as the section name of the XDP program used.
To represent a packet consisting of multiple frames, a new flag called
XDP_PKT_CONTD is introduced in the options field of the Rx and Tx
descriptors. If it is true (1) the packet continues with the next
descriptor and if it is false (0) it means this is the last descriptor
of the packet. Why the reverse logic of end-of-packet (eop) flag found
in many NICs? Just to preserve compatibility with non-multi-buffer
applications that have this bit set to false for all packets on Rx,
and the apps set the options field to zero for Tx, as anything else
will be treated as an invalid descriptor.
These are the semantics for producing packets onto AF_XDP Tx ring
consisting of multiple frames:
* When an invalid descriptor is found, all the other
descriptors/frames of this packet are marked as invalid and not
completed. The next descriptor is treated as the start of a new
packet, even if this was not the intent (because we cannot guess
the intent). As before, if your program is producing invalid
descriptors you have a bug that must be fixed.
* Zero length descriptors are treated as invalid descriptors.
* For copy mode, the maximum supported number of frames in a packet is
equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all
descriptors accumulated so far are dropped and treated as
invalid. To produce an application that will work on any system
regardless of this config setting, limit the number of frags to 18,
as the minimum value of the config is 17.
* For zero-copy mode, the limit is up to what the NIC HW
supports. Usually at least five on the NICs we have checked. We
consciously chose to not enforce a rigid limit (such as
CONFIG_MAX_SKB_FRAGS + 1) for zero-copy mode, as it would have
resulted in copy actions under the hood to fit into what limit the
NIC supports. Kind of defeats the purpose of zero-copy mode. How to
probe for this limit is explained in the "probe for multi-buffer
support" section.
On the Rx path in copy-mode, the xsk core copies the XDP data into
multiple descriptors, if needed, and sets the XDP_PKT_CONTD flag as
detailed before. Zero-copy mode works the same, though the data is not
copied. When the application gets a descriptor with the XDP_PKT_CONTD
flag set to one, it means that the packet consists of multiple buffers
and it continues with the next buffer in the following
descriptor. When a descriptor with XDP_PKT_CONTD == 0 is received, it
means that this is the last buffer of the packet. AF_XDP guarantees
that only a complete packet (all frames in the packet) is sent to the
application. If there is not enough space in the AF_XDP Rx ring, all
frames of the packet will be dropped.
If application reads a batch of descriptors, using for example the libxdp
interfaces, it is not guaranteed that the batch will end with a full
packet. It might end in the middle of a packet and the rest of the
buffers of that packet will arrive at the beginning of the next batch,
since the libxdp interface does not read the whole ring (unless you
have an enormous batch size or a very small ring size).
An example program each for Rx and Tx multi-buffer support can be found
later in this document.
Usage
=====
-----
In order to use AF_XDP sockets two parts are needed. The
user-space application and the XDP program. For a complete setup and
@ -541,6 +625,131 @@ like this:
But please use the libbpf functions as they are optimized and ready to
use. Will make your life easier.
Usage Multi-Buffer Rx
---------------------
Here is a simple Rx path pseudo-code example (using libxdp interfaces
for simplicity). Error paths have been excluded to keep it short:
.. code-block:: c
void rx_packets(struct xsk_socket_info *xsk)
{
static bool new_packet = true;
u32 idx_rx = 0, idx_fq = 0;
static char *pkt;
int rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
for (int i = 0; i < rcvd; i++) {
struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
char *frag = xsk_umem__get_data(xsk->umem->buffer, desc->addr);
bool eop = !(desc->options & XDP_PKT_CONTD);
if (new_packet)
pkt = frag;
else
add_frag_to_pkt(pkt, frag);
if (eop)
process_pkt(pkt);
new_packet = eop;
*xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = desc->addr;
}
xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
xsk_ring_cons__release(&xsk->rx, rcvd);
}
Usage Multi-Buffer Tx
---------------------
Here is an example Tx path pseudo-code (using libxdp interfaces for
simplicity) ignoring that the umem is finite in size, and that we
eventually will run out of packets to send. Also assumes pkts.addr
points to a valid location in the umem.
.. code-block:: c
void tx_packets(struct xsk_socket_info *xsk, struct pkt *pkts,
int batch_size)
{
u32 idx, i, pkt_nb = 0;
xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx);
for (i = 0; i < batch_size;) {
u64 addr = pkts[pkt_nb].addr;
u32 len = pkts[pkt_nb].size;
do {
struct xdp_desc *tx_desc;
tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i++);
tx_desc->addr = addr;
if (len > xsk_frame_size) {
tx_desc->len = xsk_frame_size;
tx_desc->options = XDP_PKT_CONTD;
} else {
tx_desc->len = len;
tx_desc->options = 0;
pkt_nb++;
}
len -= tx_desc->len;
addr += xsk_frame_size;
if (i == batch_size) {
/* Remember len, addr, pkt_nb for next iteration.
* Skipped for simplicity.
*/
break;
}
} while (len);
}
xsk_ring_prod__submit(&xsk->tx, i);
}
Probing for Multi-Buffer Support
--------------------------------
To discover if a driver supports multi-buffer AF_XDP in SKB or DRV
mode, use the XDP_FEATURES feature of netlink in linux/netdev.h to
query for NETDEV_XDP_ACT_RX_SG support. This is the same flag as for
querying for XDP multi-buffer support. If XDP supports multi-buffer in
a driver, then AF_XDP will also support that in SKB and DRV mode.
To discover if a driver supports multi-buffer AF_XDP in zero-copy
mode, use XDP_FEATURES and first check the NETDEV_XDP_ACT_XSK_ZEROCOPY
flag. If it is set, it means that at least zero-copy is supported and
you should go and check the netlink attribute
NETDEV_A_DEV_XDP_ZC_MAX_SEGS in linux/netdev.h. An unsigned integer
value will be returned stating the max number of frags that are
supported by this device in zero-copy mode. These are the possible
return values:
1: Multi-buffer for zero-copy is not supported by this device, as max
one fragment supported means that multi-buffer is not possible.
>=2: Multi-buffer is supported in zero-copy mode for this device. The
returned number signifies the max number of frags supported.
For an example on how these are used through libbpf, please take a
look at tools/testing/selftests/bpf/xskxceiver.c.
Multi-Buffer Support for Zero-Copy Drivers
------------------------------------------
Zero-copy drivers usually use the batched APIs for Rx and Tx
processing. Note that the Tx batch API guarantees that it will provide
a batch of Tx descriptors that ends with full packet at the end. This
to facilitate extending a zero-copy driver with multi-buffer support.
Sample application
==================

View File

@ -52,6 +52,15 @@ Descriptor Formats
GVE supports two descriptor formats: GQI and DQO. These two formats have
entirely different descriptors, which will be described below.
Addressing Mode
------------------
GVE supports two addressing modes: QPL and RDA.
QPL ("queue-page-list") mode communicates data through a set of
pre-registered pages.
For RDA ("raw DMA addressing") mode, the set of pages is dynamic.
Therefore, the packet buffers can be anywhere in guest memory.
Registers
---------
All registers are MMIO.

View File

@ -332,3 +332,11 @@ Setup HTB offload
# tc class add dev <interface> parent 1: classid 1:1 htb rate 10Gbit prio 1
# tc class add dev <interface> parent 1: classid 1:2 htb rate 10Gbit prio 7
4. Create tc classes with same priorities and different quantum::
# tc class add dev <interface> parent 1: classid 1:1 htb rate 10Gbit prio 2 quantum 409600
# tc class add dev <interface> parent 1: classid 1:2 htb rate 10Gbit prio 2 quantum 188416
# tc class add dev <interface> parent 1: classid 1:3 htb rate 10Gbit prio 2 quantum 32768

View File

@ -346,6 +346,24 @@ the software port.
- The number of receive packets with CQE compression on ring i [#accel]_.
- Acceleration
* - `rx[i]_arfs_add`
- The number of aRFS flow rules added to the device for direct RQ steering
on ring i [#accel]_.
- Acceleration
* - `rx[i]_arfs_request_in`
- Number of flow rules that have been requested to move into ring i for
direct RQ steering [#accel]_.
- Acceleration
* - `rx[i]_arfs_request_out`
- Number of flow rules that have been requested to move out of ring i [#accel]_.
- Acceleration
* - `rx[i]_arfs_expired`
- Number of flow rules that have been expired and removed [#accel]_.
- Acceleration
* - `rx[i]_arfs_err`
- Number of flow rules that failed to be added to the flow table.
- Error
@ -445,11 +463,6 @@ the software port.
context.
- Error
* - `rx[i]_xsk_arfs_err`
- aRFS (accelerated Receive Flow Steering) does not occur in the XSK RQ
context, so this counter should never increment.
- Error
* - `rx[i]_xdp_tx_xmit`
- The number of packets forwarded back to the port due to XDP program
`XDP_TX` action (bouncing). these packets are not counted by other
@ -683,6 +696,12 @@ the software port.
time protocol.
- Error
* - `ptp_cq[i]_late_cqe`
- Number of times a CQE has been delivered on the PTP timestamping CQ when
the CQE was not expected since a certain amount of time had elapsed where
the device typically ensures not posting the CQE.
- Error
.. [#ring_global] The corresponding ring and global counters do not share the
same name (i.e. do not follow the common naming scheme).

View File

@ -1,313 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
.. include:: <isonum.txt>
=======
Devlink
=======
:Copyright: |copy| 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
Contents
========
- `Info`_
- `Parameters`_
- `Health reporters`_
Info
====
The devlink info reports the running and stored firmware versions on device.
It also prints the device PSID which represents the HCA board type ID.
User command example::
$ devlink dev info pci/0000:00:06.0
pci/0000:00:06.0:
driver mlx5_core
versions:
fixed:
fw.psid MT_0000000009
running:
fw.version 16.26.0100
stored:
fw.version 16.26.0100
Parameters
==========
flow_steering_mode: Device flow steering mode
---------------------------------------------
The flow steering mode parameter 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.
SMFS mode is faster and provides better rule insertion rate compared to default DMFS mode.
User command examples:
- Set SMFS flow steering mode::
$ devlink dev param set pci/0000:06:00.0 name flow_steering_mode value "smfs" cmode runtime
- Read device flow steering mode::
$ devlink dev param show pci/0000:06:00.0 name flow_steering_mode
pci/0000:06:00.0:
name flow_steering_mode type driver-specific
values:
cmode runtime value smfs
enable_roce: RoCE enablement state
----------------------------------
If the device supports RoCE disablement, RoCE enablement state controls device
support for RoCE capability. Otherwise, the control occurs in the driver stack.
When RoCE is disabled at the driver level, only raw ethernet QPs are supported.
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
esw_port_metadata: Eswitch port metadata state
----------------------------------------------
When applicable, disabling eswitch metadata can increase packet rate
up to 20% depending on the use case and packet sizes.
Eswitch port metadata state controls whether to internally tag packets with
metadata. Metadata tagging must be enabled for multi-port RoCE, failover
between representors and stacked devices.
By default metadata is enabled on the supported devices in E-switch.
Metadata is applicable only for E-switch in switchdev mode and
users may disable it when NONE of the below use cases will be in use:
1. HCA is in Dual/multi-port RoCE mode.
2. VF/SF representor bonding (Usually used for Live migration)
3. Stacked devices
When metadata is disabled, the above use cases will fail to initialize if
users try to enable them.
- Show eswitch port metadata::
$ devlink dev param show pci/0000:06:00.0 name esw_port_metadata
pci/0000:06:00.0:
name esw_port_metadata type driver-specific
values:
cmode runtime value true
- Disable eswitch port metadata::
$ devlink dev param set pci/0000:06:00.0 name esw_port_metadata value false cmode runtime
- Change eswitch mode to switchdev mode where after choosing the metadata value::
$ devlink dev eswitch set pci/0000:06:00.0 mode switchdev
hairpin_num_queues: Number of hairpin queues
--------------------------------------------
We refer to a TC NIC rule that involves forwarding as "hairpin".
Hairpin queues are mlx5 hardware specific implementation for hardware
forwarding of such packets.
- Show the number of hairpin queues::
$ devlink dev param show pci/0000:06:00.0 name hairpin_num_queues
pci/0000:06:00.0:
name hairpin_num_queues type driver-specific
values:
cmode driverinit value 2
- Change the number of hairpin queues::
$ devlink dev param set pci/0000:06:00.0 name hairpin_num_queues value 4 cmode driverinit
hairpin_queue_size: Size of the hairpin queues
----------------------------------------------
Control the size of the hairpin queues.
- Show the size of the hairpin queues::
$ devlink dev param show pci/0000:06:00.0 name hairpin_queue_size
pci/0000:06:00.0:
name hairpin_queue_size type driver-specific
values:
cmode driverinit value 1024
- Change the size (in packets) of the hairpin queues::
$ devlink dev param set pci/0000:06:00.0 name hairpin_queue_size value 512 cmode driverinit
Health reporters
================
tx reporter
-----------
The tx reporter is responsible for reporting and recovering of the following two error scenarios:
- tx timeout
Report on kernel tx timeout detection.
Recover by searching lost interrupts.
- tx error completion
Report on error tx completion.
Recover by flushing the tx queue and reset it.
tx reporter also support on demand diagnose callback, on which it provides
real time information of its send queues status.
User commands examples:
- Diagnose send queues status::
$ devlink health diagnose pci/0000:82:00.0 reporter tx
.. note::
This command has valid output only when interface is up, otherwise the command has empty output.
- Show number of tx errors indicated, number of recover flows ended successfully,
is autorecover enabled and graceful period from last recover::
$ devlink health show pci/0000:82:00.0 reporter tx
rx reporter
-----------
The rx reporter is responsible for reporting and recovering of the following two error scenarios:
- rx queues' initialization (population) timeout
Population of rx queues' descriptors on ring initialization is done
in napi context via triggering an irq. In case of a failure to get
the minimum amount of descriptors, a timeout would occur, and
descriptors could be recovered by polling the EQ (Event Queue).
- rx completions with errors (reported by HW on interrupt context)
Report on rx completion error.
Recover (if needed) by flushing the related queue and reset it.
rx reporter also supports on demand diagnose callback, on which it
provides real time information of its receive queues' status.
- Diagnose rx queues' status and corresponding completion queue::
$ devlink health diagnose pci/0000:82:00.0 reporter rx
NOTE: This command has valid output only when interface is up. Otherwise, the command has empty output.
- Show number of rx errors indicated, number of recover flows ended successfully,
is autorecover enabled, and graceful period from last recover::
$ devlink health show pci/0000:82:00.0 reporter rx
fw reporter
-----------
The fw reporter implements `diagnose` and `dump` callbacks.
It follows symptoms of fw error such as fw syndrome by triggering
fw core dump and storing it into the dump buffer.
The fw reporter diagnose command can be triggered any time by the user to check
current fw status.
User commands examples:
- Check fw heath status::
$ devlink health diagnose pci/0000:82:00.0 reporter fw
- Read FW core dump if already stored or trigger new one::
$ devlink health dump show pci/0000:82:00.0 reporter fw
.. note::
This command can run only on the PF which has fw tracer ownership,
running it on other PF or any VF will return "Operation not permitted".
fw fatal reporter
-----------------
The fw fatal reporter implements `dump` and `recover` callbacks.
It follows fatal errors indications by CR-space dump and recover flow.
The CR-space dump uses vsc interface which is valid even if the FW command
interface is not functional, which is the case in most FW fatal errors.
The recover function runs recover flow which reloads the driver and triggers fw
reset if needed.
On firmware error, the health buffer is dumped into the dmesg. The log
level is derived from the error's severity (given in health buffer).
User commands examples:
- Run fw recover flow manually::
$ devlink health recover pci/0000:82:00.0 reporter fw_fatal
- Read FW CR-space dump if already stored or trigger new one::
$ devlink health dump show pci/0000:82:00.1 reporter fw_fatal
.. note::
This command can run only on PF.
vnic reporter
-------------
The vnic reporter implements only the `diagnose` callback.
It is responsible for querying the vnic diagnostic counters from fw and displaying
them in realtime.
Description of the vnic counters:
- total_q_under_processor_handle
number of queues in an error state due to
an async error or errored command.
- send_queue_priority_update_flow
number of QP/SQ priority/SL update events.
- cq_overrun
number of times CQ entered an error state due to an overflow.
- async_eq_overrun
number of times an EQ mapped to async events was overrun.
comp_eq_overrun number of times an EQ mapped to completion events was
overrun.
- quota_exceeded_command
number of commands issued and failed due to quota exceeded.
- invalid_command
number of commands issued and failed dues to any reason other than quota
exceeded.
- nic_receive_steering_discard
number of packets that completed RX flow
steering but were discarded due to a mismatch in flow table.
- generated_pkt_steering_fail
number of packets generated by the VNIC experiencing unexpected steering
failure (at any point in steering flow).
- handled_pkt_steering_fail
number of packets handled by the VNIC experiencing unexpected steering
failure (at any point in steering flow owned by the VNIC, including the FDB
for the eswitch owner).
User commands examples:
- Diagnose PF/VF vnic counters::
$ devlink health diagnose pci/0000:82:00.1 reporter vnic
- Diagnose representor vnic counters (performed by supplying devlink port of the
representor, which can be obtained via devlink port command)::
$ devlink health diagnose pci/0000:82:00.1/65537 reporter vnic
.. note::
This command can run over all interfaces such as PF/VF and representor ports.

View File

@ -13,7 +13,6 @@ Contents:
:maxdepth: 2
kconfig
devlink
switchdev
tracepoints
counters

View File

@ -36,7 +36,7 @@ Enabling the driver and kconfig options
**CONFIG_MLX5_CORE_EN_DCB=(y/n)**:
| Enables `Data Center Bridging (DCB) Support <https://community.mellanox.com/s/article/howto-auto-config-pfc-and-ets-on-connectx-4-via-lldp-dcbx>`_.
| Enables `Data Center Bridging (DCB) Support <https://enterprise-support.nvidia.com/s/article/howto-auto-config-pfc-and-ets-on-connectx-4-via-lldp-dcbx>`_.
**CONFIG_MLX5_CORE_IPOIB=(y/n)**
@ -59,12 +59,12 @@ Enabling the driver and kconfig options
**CONFIG_MLX5_EN_ARFS=(y/n)**
| Enables Hardware-accelerated receive flow steering (arfs) support, and ntuple filtering.
| https://community.mellanox.com/s/article/howto-configure-arfs-on-connectx-4
| https://enterprise-support.nvidia.com/s/article/howto-configure-arfs-on-connectx-4
**CONFIG_MLX5_EN_IPSEC=(y/n)**
| Enables `IPSec XFRM cryptography-offload acceleration <https://support.mellanox.com/s/article/ConnectX-6DX-Bluefield-2-IPsec-HW-Full-Offload-Configuration-Guide>`_.
| Enables :ref:`IPSec XFRM cryptography-offload acceleration <xfrm_device>`.
**CONFIG_MLX5_EN_MACSEC=(y/n)**
@ -87,8 +87,8 @@ Enabling the driver and kconfig options
| Ethernet SRIOV E-Switch support in ConnectX NIC. E-Switch provides internal SRIOV packet steering
| and switching for the enabled VFs and PF in two available modes:
| 1) `Legacy SRIOV mode (L2 mac vlan steering based) <https://community.mellanox.com/s/article/howto-configure-sr-iov-for-connectx-4-connectx-5-with-kvm--ethernet-x>`_.
| 2) `Switchdev mode (eswitch offloads) <https://www.mellanox.com/related-docs/prod_software/ASAP2_Hardware_Offloading_for_vSwitches_User_Manual_v4.4.pdf>`_.
| 1) `Legacy SRIOV mode (L2 mac vlan steering based) <https://enterprise-support.nvidia.com/s/article/HowTo-Configure-SR-IOV-for-ConnectX-4-ConnectX-5-ConnectX-6-with-KVM-Ethernet>`_.
| 2) :ref:`Switchdev mode (eswitch offloads) <switchdev>`.
**CONFIG_MLX5_FPGA=(y/n)**
@ -101,13 +101,13 @@ Enabling the driver and kconfig options
**CONFIG_MLX5_INFINIBAND=(y/n/m)** (module mlx5_ib.ko)
| Provides low-level InfiniBand/RDMA and `RoCE <https://community.mellanox.com/s/article/recommended-network-configuration-examples-for-roce-deployment>`_ support.
| Provides low-level InfiniBand/RDMA and `RoCE <https://enterprise-support.nvidia.com/s/article/recommended-network-configuration-examples-for-roce-deployment>`_ support.
**CONFIG_MLX5_MPFS=(y/n)**
| Ethernet Multi-Physical Function Switch (MPFS) support in ConnectX NIC.
| MPFs is required for when `Multi-Host <http://www.mellanox.com/page/multihost>`_ configuration is enabled to allow passing
| MPFs is required for when `Multi-Host <https://www.nvidia.com/en-us/networking/multi-host/>`_ configuration is enabled to allow passing
| user configured unicast MAC addresses to the requesting PF.

View File

@ -190,6 +190,26 @@ explicitly enable the VF migratable capability.
mlx5 driver support devlink port function attr mechanism to setup migratable
capability. (refer to Documentation/networking/devlink/devlink-port.rst)
IPsec crypto capability setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
User who wants mlx5 PCI VFs to be able to perform IPsec crypto offloading need
to explicitly enable the VF ipsec_crypto capability. Enabling IPsec capability
for VFs is supported starting with ConnectX6dx devices and above. When a VF has
IPsec capability enabled, any IPsec offloading is blocked on the PF.
mlx5 driver support devlink port function attr mechanism to setup ipsec_crypto
capability. (refer to Documentation/networking/devlink/devlink-port.rst)
IPsec packet capability setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
User who wants mlx5 PCI VFs to be able to perform IPsec packet offloading need
to explicitly enable the VF ipsec_packet capability. Enabling IPsec capability
for VFs is supported starting with ConnectX6dx devices and above. When a VF has
IPsec capability enabled, any IPsec offloading is blocked on the PF.
mlx5 driver support devlink port function attr mechanism to setup ipsec_packet
capability. (refer to Documentation/networking/devlink/devlink-port.rst)
SF state setup
--------------

View File

@ -128,6 +128,12 @@ Users may also set the RoCE capability of the function using
Users may also set the function as migratable using
'devlink port function set migratable' command.
Users may also set the IPsec crypto capability of the function using
`devlink port function set ipsec_crypto` command.
Users may also set the IPsec packet capability of the function using
`devlink port function set ipsec_packet` command.
Function attributes
===================
@ -240,6 +246,55 @@ Attach VF to the VM.
Start the VM.
Perform live migration.
IPsec crypto capability setup
-----------------------------
When user enables IPsec crypto capability for a VF, user application can offload
XFRM state crypto operation (Encrypt/Decrypt) to this VF.
When IPsec crypto capability is disabled (default) for a VF, the XFRM state is
processed in software by the kernel.
- Get IPsec crypto capability of the VF device::
$ devlink port show pci/0000:06:00.0/2
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
function:
hw_addr 00:00:00:00:00:00 ipsec_crypto disabled
- Set IPsec crypto capability of the VF device::
$ devlink port function set pci/0000:06:00.0/2 ipsec_crypto enable
$ devlink port show pci/0000:06:00.0/2
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
function:
hw_addr 00:00:00:00:00:00 ipsec_crypto enabled
IPsec packet capability setup
-----------------------------
When user enables IPsec packet capability for a VF, user application can offload
XFRM state and policy crypto operation (Encrypt/Decrypt) to this VF, as well as
IPsec encapsulation.
When IPsec packet capability is disabled (default) for a VF, the XFRM state and
policy is processed in software by the kernel.
- Get IPsec packet capability of the VF device::
$ devlink port show pci/0000:06:00.0/2
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
function:
hw_addr 00:00:00:00:00:00 ipsec_packet disabled
- Set IPsec packet capability of the VF device::
$ devlink port function set pci/0000:06:00.0/2 ipsec_packet enable
$ devlink port show pci/0000:06:00.0/2
pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
function:
hw_addr 00:00:00:00:00:00 ipsec_packet enabled
Subfunction
============

View File

@ -18,6 +18,11 @@ Parameters
* - ``enable_roce``
- driverinit
- Type: Boolean
If the device supports RoCE disablement, RoCE enablement state controls
device support for RoCE capability. Otherwise, the control occurs in the
driver stack. When RoCE is disabled at the driver level, only raw
ethernet QPs are supported.
* - ``io_eq_size``
- driverinit
- The range is between 64 and 4096.
@ -48,6 +53,9 @@ parameters.
* ``smfs`` Software managed flow steering. In SMFS mode, the HW
steering entities are created and manage through the driver without
firmware intervention.
SMFS mode is faster and provides better rule insertion rate compared to
default DMFS mode.
* - ``fdb_large_groups``
- u32
- driverinit
@ -71,7 +79,24 @@ parameters.
deprecated.
Default: disabled
* - ``esw_port_metadata``
- Boolean
- runtime
- When applicable, disabling eswitch metadata can increase packet rate up
to 20% depending on the use case and packet sizes.
Eswitch port metadata state controls whether to internally tag packets
with metadata. Metadata tagging must be enabled for multi-port RoCE,
failover between representors and stacked devices. By default metadata is
enabled on the supported devices in E-switch. Metadata is applicable only
for E-switch in switchdev mode and users may disable it when NONE of the
below use cases will be in use:
1. HCA is in Dual/multi-port RoCE mode.
2. VF/SF representor bonding (Usually used for Live migration)
3. Stacked devices
When metadata is disabled, the above use cases will fail to initialize if
users try to enable them.
* - ``hairpin_num_queues``
- u32
- driverinit
@ -104,3 +129,160 @@ The ``mlx5`` driver reports the following versions
* - ``fw.version``
- stored, running
- Three digit major.minor.subminor firmware version number.
Health reporters
================
tx reporter
-----------
The tx reporter is responsible for reporting and recovering of the following three error scenarios:
- tx timeout
Report on kernel tx timeout detection.
Recover by searching lost interrupts.
- tx error completion
Report on error tx completion.
Recover by flushing the tx queue and reset it.
- tx PTP port timestamping CQ unhealthy
Report too many CQEs never delivered on port ts CQ.
Recover by flushing and re-creating all PTP channels.
tx reporter also support on demand diagnose callback, on which it provides
real time information of its send queues status.
User commands examples:
- Diagnose send queues status::
$ devlink health diagnose pci/0000:82:00.0 reporter tx
.. note::
This command has valid output only when interface is up, otherwise the command has empty output.
- Show number of tx errors indicated, number of recover flows ended successfully,
is autorecover enabled and graceful period from last recover::
$ devlink health show pci/0000:82:00.0 reporter tx
rx reporter
-----------
The rx reporter is responsible for reporting and recovering of the following two error scenarios:
- rx queues' initialization (population) timeout
Population of rx queues' descriptors on ring initialization is done
in napi context via triggering an irq. In case of a failure to get
the minimum amount of descriptors, a timeout would occur, and
descriptors could be recovered by polling the EQ (Event Queue).
- rx completions with errors (reported by HW on interrupt context)
Report on rx completion error.
Recover (if needed) by flushing the related queue and reset it.
rx reporter also supports on demand diagnose callback, on which it
provides real time information of its receive queues' status.
- Diagnose rx queues' status and corresponding completion queue::
$ devlink health diagnose pci/0000:82:00.0 reporter rx
.. note::
This command has valid output only when interface is up. Otherwise, the command has empty output.
- Show number of rx errors indicated, number of recover flows ended successfully,
is autorecover enabled, and graceful period from last recover::
$ devlink health show pci/0000:82:00.0 reporter rx
fw reporter
-----------
The fw reporter implements `diagnose` and `dump` callbacks.
It follows symptoms of fw error such as fw syndrome by triggering
fw core dump and storing it into the dump buffer.
The fw reporter diagnose command can be triggered any time by the user to check
current fw status.
User commands examples:
- Check fw heath status::
$ devlink health diagnose pci/0000:82:00.0 reporter fw
- Read FW core dump if already stored or trigger new one::
$ devlink health dump show pci/0000:82:00.0 reporter fw
.. note::
This command can run only on the PF which has fw tracer ownership,
running it on other PF or any VF will return "Operation not permitted".
fw fatal reporter
-----------------
The fw fatal reporter implements `dump` and `recover` callbacks.
It follows fatal errors indications by CR-space dump and recover flow.
The CR-space dump uses vsc interface which is valid even if the FW command
interface is not functional, which is the case in most FW fatal errors.
The recover function runs recover flow which reloads the driver and triggers fw
reset if needed.
On firmware error, the health buffer is dumped into the dmesg. The log
level is derived from the error's severity (given in health buffer).
User commands examples:
- Run fw recover flow manually::
$ devlink health recover pci/0000:82:00.0 reporter fw_fatal
- Read FW CR-space dump if already stored or trigger new one::
$ devlink health dump show pci/0000:82:00.1 reporter fw_fatal
.. note::
This command can run only on PF.
vnic reporter
-------------
The vnic reporter implements only the `diagnose` callback.
It is responsible for querying the vnic diagnostic counters from fw and displaying
them in realtime.
Description of the vnic counters:
- total_q_under_processor_handle
number of queues in an error state due to
an async error or errored command.
- send_queue_priority_update_flow
number of QP/SQ priority/SL update events.
- cq_overrun
number of times CQ entered an error state due to an overflow.
- async_eq_overrun
number of times an EQ mapped to async events was overrun.
comp_eq_overrun number of times an EQ mapped to completion events was
overrun.
- quota_exceeded_command
number of commands issued and failed due to quota exceeded.
- invalid_command
number of commands issued and failed dues to any reason other than quota
exceeded.
- nic_receive_steering_discard
number of packets that completed RX flow
steering but were discarded due to a mismatch in flow table.
- generated_pkt_steering_fail
number of packets generated by the VNIC experiencing unexpected steering
failure (at any point in steering flow).
- handled_pkt_steering_fail
number of packets handled by the VNIC experiencing unexpected steering
failure (at any point in steering flow owned by the VNIC, including the FDB
for the eswitch owner).
User commands examples:
- Diagnose PF/VF vnic counters::
$ devlink health diagnose pci/0000:82:00.1 reporter vnic
- Diagnose representor vnic counters (performed by supplying devlink port of the
representor, which can be obtained via devlink port command)::
$ devlink health diagnose pci/0000:82:00.1/65537 reporter vnic
.. note::
This command can run over all interfaces such as PF/VF and representor ports.

View File

@ -321,6 +321,7 @@ tcp_abort_on_overflow - BOOLEAN
option can harm clients of your server.
tcp_adv_win_scale - INTEGER
Obsolete since linux-6.6
Count buffering overhead as bytes/2^tcp_adv_win_scale
(if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale),
if it is <= 0.
@ -2287,6 +2288,14 @@ accept_ra_min_hop_limit - INTEGER
Default: 1
accept_ra_min_lft - INTEGER
Minimum acceptable lifetime value in Router Advertisement.
RA sections with a lifetime less than this value shall be
ignored. Zero lifetimes stay unaffected.
Default: 0
accept_ra_pinfo - BOOLEAN
Learn Prefix Information in Router Advertisement.

View File

@ -74,3 +74,11 @@ stale_loss_cnt - INTEGER
This is a per-namespace sysctl.
Default: 4
scheduler - STRING
Select the scheduler of your choice.
Support for selection of different schedulers. This is a per-namespace
sysctl.
Default: "default"

View File

@ -13,6 +13,8 @@ IPv6 support by Cong Wang <xiyou.wangcong@gmail.com>, Jan 1 2013
Extended console support by Tejun Heo <tj@kernel.org>, May 1 2015
Release prepend support by Breno Leitao <leitao@debian.org>, Jul 7 2023
Please send bug reports to Matt Mackall <mpm@selenic.com>
Satyam Sharma <satyam.sharma@gmail.com>, and Cong Wang <xiyou.wangcong@gmail.com>
@ -34,10 +36,11 @@ Sender and receiver configuration:
It takes a string configuration parameter "netconsole" in the
following format::
netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
netconsole=[+][r][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
where
+ if present, enable extended console support
r if present, prepend kernel version (release) to the message
src-port source for UDP packets (defaults to 6665)
src-ip source IP to use (interface address)
dev network interface (eth0)
@ -125,6 +128,7 @@ The interface exposes these parameters of a netconsole target to userspace:
============== ================================= ============
enabled Is this target currently enabled? (read-write)
extended Extended mode enabled (read-write)
release Prepend kernel release to message (read-write)
dev_name Local network interface name (read-write)
local_port Source UDP port to use (read-write)
remote_port Remote agent's UDP port (read-write)
@ -165,6 +169,11 @@ following format which is the same as /dev/kmsg::
<level>,<sequnum>,<timestamp>,<contflag>;<message text>
If 'r' (release) feature is enabled, the kernel release version is
prepended to the start of the message. Example::
6.4.0,6,444,501151268,-;netconsole: network logging started
Non printable characters in <message text> are escaped using "\xff"
notation. If the message contains optional dictionary, verbatim
newline is used as the delimiter.

View File

@ -4,22 +4,8 @@
Page Pool API
=============
The page_pool allocator is optimized for the XDP mode that uses one frame
per-page, but it can fallback on the regular page allocator APIs.
Basic use involves replacing alloc_pages() calls with the
page_pool_alloc_pages() call. Drivers should use page_pool_dev_alloc_pages()
replacing dev_alloc_pages().
API keeps track of in-flight pages, in order to let API user know
when it is safe to free a page_pool object. Thus, API users
must run page_pool_release_page() when a page is leaving the page_pool or
call page_pool_put_page() where appropriate in order to maintain correct
accounting.
API user must call page_pool_put_page() once on a page, as it
will either recycle the page, or in case of refcnt > 1, it will
release the DMA mapping and in-flight state accounting.
.. kernel-doc:: include/net/page_pool/helpers.h
:doc: page_pool allocator
Architecture overview
=====================
@ -64,87 +50,68 @@ This lockless guarantee naturally comes from running under a NAPI softirq.
The protection doesn't strictly have to be NAPI, any guarantee that allocating
a page will cause no race conditions is enough.
* page_pool_create(): Create a pool.
* flags: PP_FLAG_DMA_MAP, PP_FLAG_DMA_SYNC_DEV
* order: 2^order pages on allocation
* pool_size: size of the ptr_ring
* nid: preferred NUMA node for allocation
* dev: struct device. Used on DMA operations
* dma_dir: DMA direction
* max_len: max DMA sync memory size
* offset: DMA address offset
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_create
* page_pool_put_page(): The outcome of this depends on the page refcnt. If the
driver bumps the refcnt > 1 this will unmap the page. If the page refcnt is 1
the allocator owns the page and will try to recycle it in one of the pool
caches. If PP_FLAG_DMA_SYNC_DEV is set, the page will be synced for_device
using dma_sync_single_range_for_device().
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_params
* page_pool_put_full_page(): Similar to page_pool_put_page(), but will DMA sync
for the entire memory area configured in area pool->max_len.
.. kernel-doc:: include/net/page_pool/helpers.h
:identifiers: page_pool_put_page page_pool_put_full_page
page_pool_recycle_direct page_pool_dev_alloc_pages
page_pool_get_dma_addr page_pool_get_dma_dir
* page_pool_recycle_direct(): Similar to page_pool_put_full_page() but caller
must guarantee safe context (e.g NAPI), since it will recycle the page
directly into the pool fast cache.
.. kernel-doc:: net/core/page_pool.c
:identifiers: page_pool_put_page_bulk page_pool_get_stats
* page_pool_release_page(): Unmap the page (if mapped) and account for it on
in-flight counters.
DMA sync
--------
Driver is always responsible for syncing the pages for the CPU.
Drivers may choose to take care of syncing for the device as well
or set the ``PP_FLAG_DMA_SYNC_DEV`` flag to request that pages
allocated from the page pool are already synced for the device.
* page_pool_dev_alloc_pages(): Get a page from the page allocator or page_pool
caches.
If ``PP_FLAG_DMA_SYNC_DEV`` is set, the driver must inform the core what portion
of the buffer has to be synced. This allows the core to avoid syncing the entire
page when the drivers knows that the device only accessed a portion of the page.
* page_pool_get_dma_addr(): Retrieve the stored DMA address.
Most drivers will reserve headroom in front of the frame. This part
of the buffer is not touched by the device, so to avoid syncing
it drivers can set the ``offset`` field in struct page_pool_params
appropriately.
* page_pool_get_dma_dir(): Retrieve the stored DMA direction.
For pages recycled on the XDP xmit and skb paths the page pool will
use the ``max_len`` member of struct page_pool_params to decide how
much of the page needs to be synced (starting at ``offset``).
When directly freeing pages in the driver (page_pool_put_page())
the ``dma_sync_size`` argument specifies how much of the buffer needs
to be synced.
* page_pool_put_page_bulk(): Tries to refill a number of pages into the
ptr_ring cache holding ptr_ring producer lock. If the ptr_ring is full,
page_pool_put_page_bulk() will release leftover pages to the page allocator.
page_pool_put_page_bulk() is suitable to be run inside the driver NAPI tx
completion loop for the XDP_REDIRECT use case.
Please note the caller must not use data area after running
page_pool_put_page_bulk(), as this function overwrites it.
If in doubt set ``offset`` to 0, ``max_len`` to ``PAGE_SIZE`` and
pass -1 as ``dma_sync_size``. That combination of arguments is always
correct.
* page_pool_get_stats(): Retrieve statistics about the page_pool. This API
is only available if the kernel has been configured with
``CONFIG_PAGE_POOL_STATS=y``. A pointer to a caller allocated ``struct
page_pool_stats`` structure is passed to this API which is filled in. The
caller can then report those stats to the user (perhaps via ethtool,
debugfs, etc.). See below for an example usage of this API.
Note that the syncing parameters are for the entire page.
This is important to remember when using fragments (``PP_FLAG_PAGE_FRAG``),
where allocated buffers may be smaller than a full page.
Unless the driver author really understands page pool internals
it's recommended to always use ``offset = 0``, ``max_len = PAGE_SIZE``
with fragmented page pools.
Stats API and structures
------------------------
If the kernel is configured with ``CONFIG_PAGE_POOL_STATS=y``, the API
``page_pool_get_stats()`` and structures described below are available. It
takes a pointer to a ``struct page_pool`` and a pointer to a ``struct
page_pool_stats`` allocated by the caller.
page_pool_get_stats() and structures described below are available.
It takes a pointer to a ``struct page_pool`` and a pointer to a struct
page_pool_stats allocated by the caller.
The API will fill in the provided ``struct page_pool_stats`` with
The API will fill in the provided struct page_pool_stats with
statistics about the page_pool.
The stats structure has the following fields::
struct page_pool_stats {
struct page_pool_alloc_stats alloc_stats;
struct page_pool_recycle_stats recycle_stats;
};
The ``struct page_pool_alloc_stats`` has the following fields:
* ``fast``: successful fast path allocations
* ``slow``: slow path order-0 allocations
* ``slow_high_order``: slow path high order allocations
* ``empty``: ptr ring is empty, so a slow path allocation was forced.
* ``refill``: an allocation which triggered a refill of the cache
* ``waive``: pages obtained from the ptr ring that cannot be added to
the cache due to a NUMA mismatch.
The ``struct page_pool_recycle_stats`` has the following fields:
* ``cached``: recycling placed page in the page pool cache
* ``cache_full``: page pool cache was full
* ``ring``: page placed into the ptr ring
* ``ring_full``: page released from page pool because the ptr ring was full
* ``released_refcnt``: page released (and not recycled) because refcnt > 1
.. kernel-doc:: include/net/page_pool/types.h
:identifiers: struct page_pool_recycle_stats
struct page_pool_alloc_stats
struct page_pool_stats
Coding examples
===============
@ -194,7 +161,7 @@ NAPI poller
if XDP_DROP:
page_pool_recycle_direct(page_pool, page);
} else (packet_is_skb) {
page_pool_release_page(page_pool, page);
skb_mark_for_recycle(skb);
new_page = page_pool_dev_alloc_pages(page_pool);
}
}

View File

@ -323,6 +323,10 @@ Some of the interface modes are described below:
contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
interface mode has different autonegotiation and only supports full duplex.
``PHY_INTERFACE_MODE_PSGMII``
This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
SGMII lines into a single link compared to 4 on QSGMII.
Pause frames / flow control
===========================

View File

@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
.. _xfrm_device:
===============================================
XFRM device - offloading the IPsec computations

View File

@ -167,6 +167,8 @@ Asking the maintainer for status updates on your
patch is a good way to ensure your patch is ignored or pushed to the
bottom of the priority list.
.. _Changes requested:
Changes requested
~~~~~~~~~~~~~~~~~
@ -359,6 +361,10 @@ Make sure you address all the feedback in your new posting. Do not post a new
version of the code if the discussion about the previous version is still
ongoing, unless directly instructed by a reviewer.
The new version of patches should be posted as a separate thread,
not as a reply to the previous posting. Change log should include a link
to the previous posting (see :ref:`Changes requested`).
Testing
-------

View File

@ -8,11 +8,8 @@ This document describes the many additional quirks and properties
required to describe older Generic Netlink families which form
the ``genetlink-legacy`` protocol level.
The spec is a work in progress, some of the quirks are just documented
for future reference.
Specification (defined)
=======================
Specification
=============
Attribute type nests
--------------------
@ -156,16 +153,27 @@ it will be allocated 3 for the request (``a`` is the previous operation
with a request section and the value of 2) and 8 for response (``c`` is
the previous operation in the "from-kernel" direction).
Other quirks (todo)
===================
Other quirks
============
Structures
----------
Legacy families can define C structures both to be used as the contents of
an attribute and as a fixed message header. Structures are defined in
``definitions`` and referenced in operations or attributes. Note that
structures defined in YAML are implicitly packed according to C
``definitions`` and referenced in operations or attributes.
members
~~~~~~~
- ``name`` - The attribute name of the struct member
- ``type`` - One of the scalar types ``u8``, ``u16``, ``u32``, ``u64``, ``s8``,
``s16``, ``s32``, ``s64``, ``string`` or ``binary``.
- ``byte-order`` - ``big-endian`` or ``little-endian``
- ``doc``, ``enum``, ``enum-as-flags``, ``display-hint`` - Same as for
:ref:`attribute definitions <attribute_properties>`
Note that structures defined in YAML are implicitly packed according to C
conventions. For example, the following struct is 4 bytes, not 6 bytes:
.. code-block:: c

View File

@ -14,5 +14,6 @@ Netlink documentation for users.
specs
c-code-gen
genetlink-legacy
netlink-raw
See also :ref:`Documentation/core-api/netlink.rst <kernel_netlink>`.

View File

@ -0,0 +1,58 @@
.. SPDX-License-Identifier: BSD-3-Clause
======================================================
Netlink specification support for raw Netlink families
======================================================
This document describes the additional properties required by raw Netlink
families such as ``NETLINK_ROUTE`` which use the ``netlink-raw`` protocol
specification.
Specification
=============
The netlink-raw schema extends the :doc:`genetlink-legacy <genetlink-legacy>`
schema with properties that are needed to specify the protocol numbers and
multicast IDs used by raw netlink families. See :ref:`classic_netlink` for more
information.
Globals
-------
protonum
~~~~~~~~
The ``protonum`` property is used to specify the protocol number to use when
opening a netlink socket.
.. code-block:: yaml
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: rt-addr
protocol: netlink-raw
protonum: 0 # part of the NETLINK_ROUTE protocol
Multicast group properties
--------------------------
value
~~~~~
The ``value`` property is used to specify the group ID to use for multicast
group registration.
.. code-block:: yaml
mcast-groups:
list:
-
name: rtnlgrp-ipv4-ifaddr
value: 5
-
name: rtnlgrp-ipv6-ifaddr
value: 9
-
name: rtnlgrp-mctp-ifaddr
value: 34

View File

@ -68,6 +68,10 @@ The following sections describe the properties of the most modern ``genetlink``
schema. See the documentation of :doc:`genetlink-c <c-code-gen>`
for information on how C names are derived from name properties.
See also :ref:`Documentation/core-api/netlink.rst <kernel_netlink>` for
information on the Netlink specification properties that are only relevant to
the kernel space and not part of the user space API.
genetlink
=========
@ -180,6 +184,8 @@ attributes
List of attributes in the set.
.. _attribute_properties:
Attribute properties
--------------------
@ -264,6 +270,13 @@ a C array of u32 values can be specified with ``type: binary`` and
``sub-type: u32``. Binary types and legacy array formats are described in
more detail in :doc:`genetlink-legacy`.
display-hint
~~~~~~~~~~~~
Optional format indicator that is intended only for choosing the right
formatting mechanism when displaying values of this type. Currently supported
hints are ``hex``, ``mac``, ``fddi``, ``ipv4``, ``ipv6`` and ``uuid``.
operations
----------

View File

@ -3697,6 +3697,7 @@ F: include/linux/filter.h
F: include/linux/tnum.h
F: kernel/bpf/core.c
F: kernel/bpf/dispatcher.c
F: kernel/bpf/mprog.c
F: kernel/bpf/syscall.c
F: kernel/bpf/tnum.c
F: kernel/bpf/trampoline.c
@ -3707,7 +3708,7 @@ R: David Vernet <void@manifault.com>
L: bpf@vger.kernel.org
L: bpf@ietf.org
S: Maintained
F: Documentation/bpf/instruction-set.rst
F: Documentation/bpf/standardization/
BPF [GENERAL] (Safe Dynamic Programs and Tools)
M: Alexei Starovoitov <ast@kernel.org>
@ -3715,7 +3716,7 @@ M: Daniel Borkmann <daniel@iogearbox.net>
M: Andrii Nakryiko <andrii@kernel.org>
R: Martin KaFai Lau <martin.lau@linux.dev>
R: Song Liu <song@kernel.org>
R: Yonghong Song <yhs@fb.com>
R: Yonghong Song <yonghong.song@linux.dev>
R: John Fastabend <john.fastabend@gmail.com>
R: KP Singh <kpsingh@kernel.org>
R: Stanislav Fomichev <sdf@google.com>
@ -3754,7 +3755,7 @@ F: tools/lib/bpf/
F: tools/testing/selftests/bpf/
BPF [ITERATOR]
M: Yonghong Song <yhs@fb.com>
M: Yonghong Song <yonghong.song@linux.dev>
L: bpf@vger.kernel.org
S: Maintained
F: kernel/bpf/*iter.c
@ -3790,13 +3791,15 @@ L: netdev@vger.kernel.org
S: Maintained
F: kernel/bpf/bpf_struct*
BPF [NETWORKING] (tc BPF, sock_addr)
BPF [NETWORKING] (tcx & tc BPF, sock_addr)
M: Martin KaFai Lau <martin.lau@linux.dev>
M: Daniel Borkmann <daniel@iogearbox.net>
R: John Fastabend <john.fastabend@gmail.com>
L: bpf@vger.kernel.org
L: netdev@vger.kernel.org
S: Maintained
F: include/net/tcx.h
F: kernel/bpf/tcx.c
F: net/core/filter.c
F: net/sched/act_bpf.c
F: net/sched/cls_bpf.c
@ -3848,6 +3851,15 @@ S: Maintained
F: kernel/bpf/stackmap.c
F: kernel/trace/bpf_trace.c
BROADCOM ASP 2.0 ETHERNET DRIVER
M: Justin Chen <justin.chen@broadcom.com>
M: Florian Fainelli <florian.fainelli@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
L: netdev@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/net/brcm,asp-v2.0.yaml
F: drivers/net/ethernet/broadcom/asp2/
BROADCOM B44 10/100 ETHERNET DRIVER
M: Michael Chan <michael.chan@broadcom.com>
L: netdev@vger.kernel.org
@ -7610,6 +7622,13 @@ L: linux-mmc@vger.kernel.org
S: Supported
F: drivers/mmc/host/cqhci*
EMS CPC-PCI CAN DRIVER
M: Gerhard Uttenthaler <uttenthaler@ems-wuensche.com>
M: support@ems-wuensche.com
L: linux-can@vger.kernel.org
S: Maintained
F: drivers/net/can/sja1000/ems_pci.c
EMULEX 10Gbps iSCSI - OneConnect DRIVER
M: Ketan Mukadam <ketan.mukadam@broadcom.com>
L: linux-scsi@vger.kernel.org
@ -7749,6 +7768,7 @@ F: include/linux/mii.h
F: include/linux/of_net.h
F: include/linux/phy.h
F: include/linux/phy_fixed.h
F: include/linux/phylib_stubs.h
F: include/linux/platform_data/mdio-bcm-unimac.h
F: include/linux/platform_data/mdio-gpio.h
F: include/trace/events/mdio.h
@ -8368,7 +8388,6 @@ L: linuxppc-dev@lists.ozlabs.org
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/freescale/fs_enet/
F: include/linux/fs_enet_pd.h
FREESCALE SOC SOUND DRIVERS
M: Shengjiu Wang <shengjiu.wang@gmail.com>
@ -14684,7 +14703,7 @@ F: drivers/rtc/rtc-ntxec.c
F: include/linux/mfd/ntxec.h
NETRONOME ETHERNET DRIVERS
M: Simon Horman <simon.horman@corigine.com>
M: Louis Peens <louis.peens@corigine.com>
R: Jakub Kicinski <kuba@kernel.org>
L: oss-drivers@corigine.com
S: Maintained
@ -16057,7 +16076,7 @@ M: Ilias Apalodimas <ilias.apalodimas@linaro.org>
L: netdev@vger.kernel.org
S: Supported
F: Documentation/networking/page_pool.rst
F: include/net/page_pool.h
F: include/net/page_pool/
F: include/trace/events/page_pool.h
F: net/core/page_pool.c
@ -17210,6 +17229,13 @@ F: drivers/ptp/*
F: include/linux/ptp_cl*
K: (?:\b|_)ptp(?:\b|_)
PTP MOCKUP CLOCK SUPPORT
M: Vladimir Oltean <vladimir.oltean@nxp.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/ptp/ptp_mock.c
F: include/linux/ptp_mock.h
PTP VIRTUAL CLOCK SUPPORT
M: Yangbo Lu <yangbo.lu@nxp.com>
L: netdev@vger.kernel.org
@ -22972,6 +22998,7 @@ S: Maintained
W: https://www.net-swift.com
F: Documentation/networking/device_drivers/ethernet/wangxun/*
F: drivers/net/ethernet/wangxun/
F: drivers/net/pcs/pcs-xpcs-wx.c
WATCHDOG DEVICE DRIVERS
M: Wim Van Sebroeck <wim@linux-watchdog.org>

View File

@ -186,6 +186,8 @@ enum aarch64_insn_ldst_type {
AARCH64_INSN_LDST_LOAD_ACQ_EX,
AARCH64_INSN_LDST_STORE_EX,
AARCH64_INSN_LDST_STORE_REL_EX,
AARCH64_INSN_LDST_SIGNED_LOAD_IMM_OFFSET,
AARCH64_INSN_LDST_SIGNED_LOAD_REG_OFFSET,
};
enum aarch64_insn_adsb_type {
@ -324,6 +326,7 @@ __AARCH64_INSN_FUNCS(prfm, 0x3FC00000, 0x39800000)
__AARCH64_INSN_FUNCS(prfm_lit, 0xFF000000, 0xD8000000)
__AARCH64_INSN_FUNCS(store_imm, 0x3FC00000, 0x39000000)
__AARCH64_INSN_FUNCS(load_imm, 0x3FC00000, 0x39400000)
__AARCH64_INSN_FUNCS(signed_load_imm, 0X3FC00000, 0x39800000)
__AARCH64_INSN_FUNCS(store_pre, 0x3FE00C00, 0x38000C00)
__AARCH64_INSN_FUNCS(load_pre, 0x3FE00C00, 0x38400C00)
__AARCH64_INSN_FUNCS(store_post, 0x3FE00C00, 0x38000400)
@ -337,6 +340,7 @@ __AARCH64_INSN_FUNCS(ldset, 0x3F20FC00, 0x38203000)
__AARCH64_INSN_FUNCS(swp, 0x3F20FC00, 0x38208000)
__AARCH64_INSN_FUNCS(cas, 0x3FA07C00, 0x08A07C00)
__AARCH64_INSN_FUNCS(ldr_reg, 0x3FE0EC00, 0x38606800)
__AARCH64_INSN_FUNCS(signed_ldr_reg, 0X3FE0FC00, 0x38A0E800)
__AARCH64_INSN_FUNCS(ldr_imm, 0x3FC00000, 0x39400000)
__AARCH64_INSN_FUNCS(ldr_lit, 0xBF000000, 0x18000000)
__AARCH64_INSN_FUNCS(ldrsw_lit, 0xFF000000, 0x98000000)

View File

@ -385,6 +385,9 @@ u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,
case AARCH64_INSN_LDST_LOAD_REG_OFFSET:
insn = aarch64_insn_get_ldr_reg_value();
break;
case AARCH64_INSN_LDST_SIGNED_LOAD_REG_OFFSET:
insn = aarch64_insn_get_signed_ldr_reg_value();
break;
case AARCH64_INSN_LDST_STORE_REG_OFFSET:
insn = aarch64_insn_get_str_reg_value();
break;
@ -430,6 +433,9 @@ u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
case AARCH64_INSN_LDST_LOAD_IMM_OFFSET:
insn = aarch64_insn_get_ldr_imm_value();
break;
case AARCH64_INSN_LDST_SIGNED_LOAD_IMM_OFFSET:
insn = aarch64_insn_get_signed_load_imm_value();
break;
case AARCH64_INSN_LDST_STORE_IMM_OFFSET:
insn = aarch64_insn_get_str_imm_value();
break;

View File

@ -59,10 +59,13 @@
AARCH64_INSN_LDST_##type##_REG_OFFSET)
#define A64_STRB(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 8, STORE)
#define A64_LDRB(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 8, LOAD)
#define A64_LDRSB(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 8, SIGNED_LOAD)
#define A64_STRH(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 16, STORE)
#define A64_LDRH(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 16, LOAD)
#define A64_LDRSH(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 16, SIGNED_LOAD)
#define A64_STR32(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 32, STORE)
#define A64_LDR32(Wt, Xn, Xm) A64_LS_REG(Wt, Xn, Xm, 32, LOAD)
#define A64_LDRSW(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 32, SIGNED_LOAD)
#define A64_STR64(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 64, STORE)
#define A64_LDR64(Xt, Xn, Xm) A64_LS_REG(Xt, Xn, Xm, 64, LOAD)
@ -73,10 +76,13 @@
AARCH64_INSN_LDST_##type##_IMM_OFFSET)
#define A64_STRBI(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 8, STORE)
#define A64_LDRBI(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 8, LOAD)
#define A64_LDRSBI(Xt, Xn, imm) A64_LS_IMM(Xt, Xn, imm, 8, SIGNED_LOAD)
#define A64_STRHI(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 16, STORE)
#define A64_LDRHI(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 16, LOAD)
#define A64_LDRSHI(Xt, Xn, imm) A64_LS_IMM(Xt, Xn, imm, 16, SIGNED_LOAD)
#define A64_STR32I(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 32, STORE)
#define A64_LDR32I(Wt, Xn, imm) A64_LS_IMM(Wt, Xn, imm, 32, LOAD)
#define A64_LDRSWI(Xt, Xn, imm) A64_LS_IMM(Xt, Xn, imm, 32, SIGNED_LOAD)
#define A64_STR64I(Xt, Xn, imm) A64_LS_IMM(Xt, Xn, imm, 64, STORE)
#define A64_LDR64I(Xt, Xn, imm) A64_LS_IMM(Xt, Xn, imm, 64, LOAD)
@ -186,6 +192,11 @@
#define A64_UXTH(sf, Rd, Rn) A64_UBFM(sf, Rd, Rn, 0, 15)
#define A64_UXTW(sf, Rd, Rn) A64_UBFM(sf, Rd, Rn, 0, 31)
/* Sign extend */
#define A64_SXTB(sf, Rd, Rn) A64_SBFM(sf, Rd, Rn, 0, 7)
#define A64_SXTH(sf, Rd, Rn) A64_SBFM(sf, Rd, Rn, 0, 15)
#define A64_SXTW(sf, Rd, Rn) A64_SBFM(sf, Rd, Rn, 0, 31)
/* Move wide (immediate) */
#define A64_MOVEW(sf, Rd, imm16, shift, type) \
aarch64_insn_gen_movewide(Rd, imm16, shift, \
@ -223,6 +234,7 @@
#define A64_DATA2(sf, Rd, Rn, Rm, type) aarch64_insn_gen_data2(Rd, Rn, Rm, \
A64_VARIANT(sf), AARCH64_INSN_DATA2_##type)
#define A64_UDIV(sf, Rd, Rn, Rm) A64_DATA2(sf, Rd, Rn, Rm, UDIV)
#define A64_SDIV(sf, Rd, Rn, Rm) A64_DATA2(sf, Rd, Rn, Rm, SDIV)
#define A64_LSLV(sf, Rd, Rn, Rm) A64_DATA2(sf, Rd, Rn, Rm, LSLV)
#define A64_LSRV(sf, Rd, Rn, Rm) A64_DATA2(sf, Rd, Rn, Rm, LSRV)
#define A64_ASRV(sf, Rd, Rn, Rm) A64_DATA2(sf, Rd, Rn, Rm, ASRV)

View File

@ -715,7 +715,8 @@ static int add_exception_handler(const struct bpf_insn *insn,
/* First pass */
return 0;
if (BPF_MODE(insn->code) != BPF_PROBE_MEM)
if (BPF_MODE(insn->code) != BPF_PROBE_MEM &&
BPF_MODE(insn->code) != BPF_PROBE_MEMSX)
return 0;
if (!ctx->prog->aux->extable ||
@ -779,12 +780,26 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
u8 dst_adj;
int off_adj;
int ret;
bool sign_extend;
switch (code) {
/* dst = src */
case BPF_ALU | BPF_MOV | BPF_X:
case BPF_ALU64 | BPF_MOV | BPF_X:
emit(A64_MOV(is64, dst, src), ctx);
switch (insn->off) {
case 0:
emit(A64_MOV(is64, dst, src), ctx);
break;
case 8:
emit(A64_SXTB(is64, dst, src), ctx);
break;
case 16:
emit(A64_SXTH(is64, dst, src), ctx);
break;
case 32:
emit(A64_SXTW(is64, dst, src), ctx);
break;
}
break;
/* dst = dst OP src */
case BPF_ALU | BPF_ADD | BPF_X:
@ -813,11 +828,17 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
break;
case BPF_ALU | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_DIV | BPF_X:
emit(A64_UDIV(is64, dst, dst, src), ctx);
if (!off)
emit(A64_UDIV(is64, dst, dst, src), ctx);
else
emit(A64_SDIV(is64, dst, dst, src), ctx);
break;
case BPF_ALU | BPF_MOD | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
emit(A64_UDIV(is64, tmp, dst, src), ctx);
if (!off)
emit(A64_UDIV(is64, tmp, dst, src), ctx);
else
emit(A64_SDIV(is64, tmp, dst, src), ctx);
emit(A64_MSUB(is64, dst, dst, tmp, src), ctx);
break;
case BPF_ALU | BPF_LSH | BPF_X:
@ -840,11 +861,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
/* dst = BSWAP##imm(dst) */
case BPF_ALU | BPF_END | BPF_FROM_LE:
case BPF_ALU | BPF_END | BPF_FROM_BE:
case BPF_ALU64 | BPF_END | BPF_FROM_LE:
#ifdef CONFIG_CPU_BIG_ENDIAN
if (BPF_SRC(code) == BPF_FROM_BE)
if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_BE)
goto emit_bswap_uxt;
#else /* !CONFIG_CPU_BIG_ENDIAN */
if (BPF_SRC(code) == BPF_FROM_LE)
if (BPF_CLASS(code) == BPF_ALU && BPF_SRC(code) == BPF_FROM_LE)
goto emit_bswap_uxt;
#endif
switch (imm) {
@ -943,12 +965,18 @@ emit_bswap_uxt:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU64 | BPF_DIV | BPF_K:
emit_a64_mov_i(is64, tmp, imm, ctx);
emit(A64_UDIV(is64, dst, dst, tmp), ctx);
if (!off)
emit(A64_UDIV(is64, dst, dst, tmp), ctx);
else
emit(A64_SDIV(is64, dst, dst, tmp), ctx);
break;
case BPF_ALU | BPF_MOD | BPF_K:
case BPF_ALU64 | BPF_MOD | BPF_K:
emit_a64_mov_i(is64, tmp2, imm, ctx);
emit(A64_UDIV(is64, tmp, dst, tmp2), ctx);
if (!off)
emit(A64_UDIV(is64, tmp, dst, tmp2), ctx);
else
emit(A64_SDIV(is64, tmp, dst, tmp2), ctx);
emit(A64_MSUB(is64, dst, dst, tmp, tmp2), ctx);
break;
case BPF_ALU | BPF_LSH | BPF_K:
@ -966,7 +994,11 @@ emit_bswap_uxt:
/* JUMP off */
case BPF_JMP | BPF_JA:
jmp_offset = bpf2a64_offset(i, off, ctx);
case BPF_JMP32 | BPF_JA:
if (BPF_CLASS(code) == BPF_JMP)
jmp_offset = bpf2a64_offset(i, off, ctx);
else
jmp_offset = bpf2a64_offset(i, imm, ctx);
check_imm26(jmp_offset);
emit(A64_B(jmp_offset), ctx);
break;
@ -1122,7 +1154,7 @@ emit_cond_jmp:
return 1;
}
/* LDX: dst = *(size *)(src + off) */
/* LDX: dst = (u64)*(unsigned size *)(src + off) */
case BPF_LDX | BPF_MEM | BPF_W:
case BPF_LDX | BPF_MEM | BPF_H:
case BPF_LDX | BPF_MEM | BPF_B:
@ -1131,6 +1163,13 @@ emit_cond_jmp:
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
case BPF_LDX | BPF_PROBE_MEM | BPF_B:
/* LDXS: dst_reg = (s64)*(signed size *)(src_reg + off) */
case BPF_LDX | BPF_MEMSX | BPF_B:
case BPF_LDX | BPF_MEMSX | BPF_H:
case BPF_LDX | BPF_MEMSX | BPF_W:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
if (ctx->fpb_offset > 0 && src == fp) {
src_adj = fpb;
off_adj = off + ctx->fpb_offset;
@ -1138,29 +1177,49 @@ emit_cond_jmp:
src_adj = src;
off_adj = off;
}
sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX ||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX);
switch (BPF_SIZE(code)) {
case BPF_W:
if (is_lsi_offset(off_adj, 2)) {
emit(A64_LDR32I(dst, src_adj, off_adj), ctx);
if (sign_extend)
emit(A64_LDRSWI(dst, src_adj, off_adj), ctx);
else
emit(A64_LDR32I(dst, src_adj, off_adj), ctx);
} else {
emit_a64_mov_i(1, tmp, off, ctx);
emit(A64_LDR32(dst, src, tmp), ctx);
if (sign_extend)
emit(A64_LDRSW(dst, src_adj, off_adj), ctx);
else
emit(A64_LDR32(dst, src, tmp), ctx);
}
break;
case BPF_H:
if (is_lsi_offset(off_adj, 1)) {
emit(A64_LDRHI(dst, src_adj, off_adj), ctx);
if (sign_extend)
emit(A64_LDRSHI(dst, src_adj, off_adj), ctx);
else
emit(A64_LDRHI(dst, src_adj, off_adj), ctx);
} else {
emit_a64_mov_i(1, tmp, off, ctx);
emit(A64_LDRH(dst, src, tmp), ctx);
if (sign_extend)
emit(A64_LDRSH(dst, src, tmp), ctx);
else
emit(A64_LDRH(dst, src, tmp), ctx);
}
break;
case BPF_B:
if (is_lsi_offset(off_adj, 0)) {
emit(A64_LDRBI(dst, src_adj, off_adj), ctx);
if (sign_extend)
emit(A64_LDRSBI(dst, src_adj, off_adj), ctx);
else
emit(A64_LDRBI(dst, src_adj, off_adj), ctx);
} else {
emit_a64_mov_i(1, tmp, off, ctx);
emit(A64_LDRB(dst, src, tmp), ctx);
if (sign_extend)
emit(A64_LDRSB(dst, src, tmp), ctx);
else
emit(A64_LDRB(dst, src, tmp), ctx);
}
break;
case BPF_DW:

View File

@ -7,7 +7,6 @@
*/
#include <linux/init.h>
#include <linux/fs_enet_pd.h>
#include <linux/of_platform.h>
#include <asm/time.h>

View File

@ -21,7 +21,6 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fs_enet_pd.h>
#include <linux/fs_uart_pd.h>
#include <linux/fsl_devices.h>
#include <linux/mii.h>

View File

@ -24,7 +24,6 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/fs_enet_pd.h>
#include <linux/fs_uart_pd.h>
#include <linux/fsl_devices.h>
#include <linux/mii.h>

View File

@ -23,7 +23,6 @@
#include <linux/phy.h>
#include <linux/spi/spi.h>
#include <linux/fsl_devices.h>
#include <linux/fs_enet_pd.h>
#include <linux/fs_uart_pd.h>
#include <linux/reboot.h>
@ -37,8 +36,6 @@
#include <asm/cpm2.h>
#include <asm/fsl_hcalls.h> /* For the Freescale hypervisor */
extern void init_fcc_ioports(struct fs_platform_info*);
extern void init_fec_ioports(struct fs_platform_info*);
extern void init_smc_ioports(struct fs_uart_platform_info*);
static phys_addr_t immrbase = -1;

View File

@ -431,11 +431,21 @@ static inline u32 rv_mulhu(u8 rd, u8 rs1, u8 rs2)
return rv_r_insn(1, rs2, rs1, 3, rd, 0x33);
}
static inline u32 rv_div(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 4, rd, 0x33);
}
static inline u32 rv_divu(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 5, rd, 0x33);
}
static inline u32 rv_rem(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 6, rd, 0x33);
}
static inline u32 rv_remu(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 7, rd, 0x33);
@ -501,6 +511,16 @@ static inline u32 rv_ble(u8 rs1, u8 rs2, u16 imm12_1)
return rv_bge(rs2, rs1, imm12_1);
}
static inline u32 rv_lb(u8 rd, u16 imm11_0, u8 rs1)
{
return rv_i_insn(imm11_0, rs1, 0, rd, 0x03);
}
static inline u32 rv_lh(u8 rd, u16 imm11_0, u8 rs1)
{
return rv_i_insn(imm11_0, rs1, 1, rd, 0x03);
}
static inline u32 rv_lw(u8 rd, u16 imm11_0, u8 rs1)
{
return rv_i_insn(imm11_0, rs1, 2, rd, 0x03);
@ -766,11 +786,21 @@ static inline u32 rv_mulw(u8 rd, u8 rs1, u8 rs2)
return rv_r_insn(1, rs2, rs1, 0, rd, 0x3b);
}
static inline u32 rv_divw(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 4, rd, 0x3b);
}
static inline u32 rv_divuw(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 5, rd, 0x3b);
}
static inline u32 rv_remw(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 6, rd, 0x3b);
}
static inline u32 rv_remuw(u8 rd, u8 rs1, u8 rs2)
{
return rv_r_insn(1, rs2, rs1, 7, rd, 0x3b);

View File

@ -13,6 +13,8 @@
#include <asm/patch.h>
#include "bpf_jit.h"
#define RV_FENTRY_NINSNS 2
#define RV_REG_TCC RV_REG_A6
#define RV_REG_TCC_SAVED RV_REG_S6 /* Store A6 in S6 if program do calls */
@ -241,7 +243,7 @@ static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
if (!is_tail_call)
emit_mv(RV_REG_A0, RV_REG_A5, ctx);
emit_jalr(RV_REG_ZERO, is_tail_call ? RV_REG_T3 : RV_REG_RA,
is_tail_call ? 20 : 0, /* skip reserved nops and TCC init */
is_tail_call ? (RV_FENTRY_NINSNS + 1) * 4 : 0, /* skip reserved nops and TCC init */
ctx);
}
@ -578,7 +580,8 @@ static int add_exception_handler(const struct bpf_insn *insn,
unsigned long pc;
off_t offset;
if (!ctx->insns || !ctx->prog->aux->extable || BPF_MODE(insn->code) != BPF_PROBE_MEM)
if (!ctx->insns || !ctx->prog->aux->extable ||
(BPF_MODE(insn->code) != BPF_PROBE_MEM && BPF_MODE(insn->code) != BPF_PROBE_MEMSX))
return 0;
if (WARN_ON_ONCE(ctx->nexentries >= ctx->prog->aux->num_exentries))
@ -618,32 +621,7 @@ static int add_exception_handler(const struct bpf_insn *insn,
return 0;
}
static int gen_call_or_nops(void *target, void *ip, u32 *insns)
{
s64 rvoff;
int i, ret;
struct rv_jit_context ctx;
ctx.ninsns = 0;
ctx.insns = (u16 *)insns;
if (!target) {
for (i = 0; i < 4; i++)
emit(rv_nop(), &ctx);
return 0;
}
rvoff = (s64)(target - (ip + 4));
emit(rv_sd(RV_REG_SP, -8, RV_REG_RA), &ctx);
ret = emit_jump_and_link(RV_REG_RA, rvoff, false, &ctx);
if (ret)
return ret;
emit(rv_ld(RV_REG_RA, -8, RV_REG_SP), &ctx);
return 0;
}
static int gen_jump_or_nops(void *target, void *ip, u32 *insns)
static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
{
s64 rvoff;
struct rv_jit_context ctx;
@ -658,38 +636,35 @@ static int gen_jump_or_nops(void *target, void *ip, u32 *insns)
}
rvoff = (s64)(target - ip);
return emit_jump_and_link(RV_REG_ZERO, rvoff, false, &ctx);
return emit_jump_and_link(is_call ? RV_REG_T0 : RV_REG_ZERO, rvoff, false, &ctx);
}
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
void *old_addr, void *new_addr)
{
u32 old_insns[4], new_insns[4];
u32 old_insns[RV_FENTRY_NINSNS], new_insns[RV_FENTRY_NINSNS];
bool is_call = poke_type == BPF_MOD_CALL;
int (*gen_insns)(void *target, void *ip, u32 *insns);
int ninsns = is_call ? 4 : 2;
int ret;
if (!is_bpf_text_address((unsigned long)ip))
if (!is_kernel_text((unsigned long)ip) &&
!is_bpf_text_address((unsigned long)ip))
return -ENOTSUPP;
gen_insns = is_call ? gen_call_or_nops : gen_jump_or_nops;
ret = gen_insns(old_addr, ip, old_insns);
ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call);
if (ret)
return ret;
if (memcmp(ip, old_insns, ninsns * 4))
if (memcmp(ip, old_insns, RV_FENTRY_NINSNS * 4))
return -EFAULT;
ret = gen_insns(new_addr, ip, new_insns);
ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
if (ret)
return ret;
cpus_read_lock();
mutex_lock(&text_mutex);
if (memcmp(ip, new_insns, ninsns * 4))
ret = patch_text(ip, new_insns, ninsns);
if (memcmp(ip, new_insns, RV_FENTRY_NINSNS * 4))
ret = patch_text(ip, new_insns, RV_FENTRY_NINSNS);
mutex_unlock(&text_mutex);
cpus_read_unlock();
@ -787,8 +762,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
int i, ret, offset;
int *branches_off = NULL;
int stack_size = 0, nregs = m->nr_args;
int retaddr_off, fp_off, retval_off, args_off;
int nregs_off, ip_off, run_ctx_off, sreg_off;
int retval_off, args_off, nregs_off, ip_off, run_ctx_off, sreg_off;
struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
@ -796,13 +770,27 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
bool save_ret;
u32 insn;
/* Generated trampoline stack layout:
/* Two types of generated trampoline stack layout:
*
* FP - 8 [ RA of parent func ] return address of parent
* 1. trampoline called from function entry
* --------------------------------------
* FP + 8 [ RA to parent func ] return address to parent
* function
* FP - retaddr_off [ RA of traced func ] return address of traced
* FP + 0 [ FP of parent func ] frame pointer of parent
* function
* FP - fp_off [ FP of parent func ]
* FP - 8 [ T0 to traced func ] return address of traced
* function
* FP - 16 [ FP of traced func ] frame pointer of traced
* function
* --------------------------------------
*
* 2. trampoline called directly
* --------------------------------------
* FP - 8 [ RA to caller func ] return address to caller
* function
* FP - 16 [ FP of caller func ] frame pointer of caller
* function
* --------------------------------------
*
* FP - retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
* BPF_TRAMP_F_RET_FENTRY_RET
@ -833,14 +821,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
if (nregs > 8)
return -ENOTSUPP;
/* room for parent function return address */
stack_size += 8;
stack_size += 8;
retaddr_off = stack_size;
stack_size += 8;
fp_off = stack_size;
/* room of trampoline frame to store return address and frame pointer */
stack_size += 16;
save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET);
if (save_ret) {
@ -867,12 +849,29 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
stack_size = round_up(stack_size, 16);
emit_addi(RV_REG_SP, RV_REG_SP, -stack_size, ctx);
if (func_addr) {
/* For the trampoline called from function entry,
* the frame of traced function and the frame of
* trampoline need to be considered.
*/
emit_addi(RV_REG_SP, RV_REG_SP, -16, ctx);
emit_sd(RV_REG_SP, 8, RV_REG_RA, ctx);
emit_sd(RV_REG_SP, 0, RV_REG_FP, ctx);
emit_addi(RV_REG_FP, RV_REG_SP, 16, ctx);
emit_sd(RV_REG_SP, stack_size - retaddr_off, RV_REG_RA, ctx);
emit_sd(RV_REG_SP, stack_size - fp_off, RV_REG_FP, ctx);
emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
emit_addi(RV_REG_SP, RV_REG_SP, -stack_size, ctx);
emit_sd(RV_REG_SP, stack_size - 8, RV_REG_T0, ctx);
emit_sd(RV_REG_SP, stack_size - 16, RV_REG_FP, ctx);
emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
} else {
/* For the trampoline called directly, just handle
* the frame of trampoline.
*/
emit_addi(RV_REG_SP, RV_REG_SP, -stack_size, ctx);
emit_sd(RV_REG_SP, stack_size - 8, RV_REG_RA, ctx);
emit_sd(RV_REG_SP, stack_size - 16, RV_REG_FP, ctx);
emit_addi(RV_REG_FP, RV_REG_SP, stack_size, ctx);
}
/* callee saved register S1 to pass start time */
emit_sd(RV_REG_FP, -sreg_off, RV_REG_S1, ctx);
@ -890,7 +889,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
/* skip to actual body of traced function */
if (flags & BPF_TRAMP_F_SKIP_FRAME)
orig_call += 16;
orig_call += RV_FENTRY_NINSNS * 4;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
emit_imm(RV_REG_A0, (const s64)im, ctx);
@ -967,17 +966,30 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
emit_ld(RV_REG_S1, -sreg_off, RV_REG_FP, ctx);
if (flags & BPF_TRAMP_F_SKIP_FRAME)
/* return address of parent function */
if (func_addr) {
/* trampoline called from function entry */
emit_ld(RV_REG_T0, stack_size - 8, RV_REG_SP, ctx);
emit_ld(RV_REG_FP, stack_size - 16, RV_REG_SP, ctx);
emit_addi(RV_REG_SP, RV_REG_SP, stack_size, ctx);
emit_ld(RV_REG_RA, 8, RV_REG_SP, ctx);
emit_ld(RV_REG_FP, 0, RV_REG_SP, ctx);
emit_addi(RV_REG_SP, RV_REG_SP, 16, ctx);
if (flags & BPF_TRAMP_F_SKIP_FRAME)
/* return to parent function */
emit_jalr(RV_REG_ZERO, RV_REG_RA, 0, ctx);
else
/* return to traced function */
emit_jalr(RV_REG_ZERO, RV_REG_T0, 0, ctx);
} else {
/* trampoline called directly */
emit_ld(RV_REG_RA, stack_size - 8, RV_REG_SP, ctx);
else
/* return address of traced function */
emit_ld(RV_REG_RA, stack_size - retaddr_off, RV_REG_SP, ctx);
emit_ld(RV_REG_FP, stack_size - 16, RV_REG_SP, ctx);
emit_addi(RV_REG_SP, RV_REG_SP, stack_size, ctx);
emit_ld(RV_REG_FP, stack_size - fp_off, RV_REG_SP, ctx);
emit_addi(RV_REG_SP, RV_REG_SP, stack_size, ctx);
emit_jalr(RV_REG_ZERO, RV_REG_RA, 0, ctx);
emit_jalr(RV_REG_ZERO, RV_REG_RA, 0, ctx);
}
ret = ctx->ninsns;
out:
@ -1035,7 +1047,19 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
emit_zext_32(rd, ctx);
break;
}
emit_mv(rd, rs, ctx);
switch (insn->off) {
case 0:
emit_mv(rd, rs, ctx);
break;
case 8:
case 16:
emit_slli(RV_REG_T1, rs, 64 - insn->off, ctx);
emit_srai(rd, RV_REG_T1, 64 - insn->off, ctx);
break;
case 32:
emit_addiw(rd, rs, 0, ctx);
break;
}
if (!is64 && !aux->verifier_zext)
emit_zext_32(rd, ctx);
break;
@ -1083,13 +1107,19 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
break;
case BPF_ALU | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_DIV | BPF_X:
emit(is64 ? rv_divu(rd, rd, rs) : rv_divuw(rd, rd, rs), ctx);
if (off)
emit(is64 ? rv_div(rd, rd, rs) : rv_divw(rd, rd, rs), ctx);
else
emit(is64 ? rv_divu(rd, rd, rs) : rv_divuw(rd, rd, rs), ctx);
if (!is64 && !aux->verifier_zext)
emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_MOD | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
emit(is64 ? rv_remu(rd, rd, rs) : rv_remuw(rd, rd, rs), ctx);
if (off)
emit(is64 ? rv_rem(rd, rd, rs) : rv_remw(rd, rd, rs), ctx);
else
emit(is64 ? rv_remu(rd, rd, rs) : rv_remuw(rd, rd, rs), ctx);
if (!is64 && !aux->verifier_zext)
emit_zext_32(rd, ctx);
break;
@ -1138,6 +1168,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
break;
case BPF_ALU | BPF_END | BPF_FROM_BE:
case BPF_ALU64 | BPF_END | BPF_FROM_LE:
emit_li(RV_REG_T2, 0, ctx);
emit_andi(RV_REG_T1, rd, 0xff, ctx);
@ -1260,16 +1291,24 @@ out_be:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU64 | BPF_DIV | BPF_K:
emit_imm(RV_REG_T1, imm, ctx);
emit(is64 ? rv_divu(rd, rd, RV_REG_T1) :
rv_divuw(rd, rd, RV_REG_T1), ctx);
if (off)
emit(is64 ? rv_div(rd, rd, RV_REG_T1) :
rv_divw(rd, rd, RV_REG_T1), ctx);
else
emit(is64 ? rv_divu(rd, rd, RV_REG_T1) :
rv_divuw(rd, rd, RV_REG_T1), ctx);
if (!is64 && !aux->verifier_zext)
emit_zext_32(rd, ctx);
break;
case BPF_ALU | BPF_MOD | BPF_K:
case BPF_ALU64 | BPF_MOD | BPF_K:
emit_imm(RV_REG_T1, imm, ctx);
emit(is64 ? rv_remu(rd, rd, RV_REG_T1) :
rv_remuw(rd, rd, RV_REG_T1), ctx);
if (off)
emit(is64 ? rv_rem(rd, rd, RV_REG_T1) :
rv_remw(rd, rd, RV_REG_T1), ctx);
else
emit(is64 ? rv_remu(rd, rd, RV_REG_T1) :
rv_remuw(rd, rd, RV_REG_T1), ctx);
if (!is64 && !aux->verifier_zext)
emit_zext_32(rd, ctx);
break;
@ -1303,7 +1342,11 @@ out_be:
/* JUMP off */
case BPF_JMP | BPF_JA:
rvoff = rv_offset(i, off, ctx);
case BPF_JMP32 | BPF_JA:
if (BPF_CLASS(code) == BPF_JMP)
rvoff = rv_offset(i, off, ctx);
else
rvoff = rv_offset(i, imm, ctx);
ret = emit_jump_and_link(RV_REG_ZERO, rvoff, true, ctx);
if (ret)
return ret;
@ -1475,7 +1518,7 @@ out_be:
return 1;
}
/* LDX: dst = *(size *)(src + off) */
/* LDX: dst = *(unsigned size *)(src + off) */
case BPF_LDX | BPF_MEM | BPF_B:
case BPF_LDX | BPF_MEM | BPF_H:
case BPF_LDX | BPF_MEM | BPF_W:
@ -1484,14 +1527,28 @@ out_be:
case BPF_LDX | BPF_PROBE_MEM | BPF_H:
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
/* LDSX: dst = *(signed size *)(src + off) */
case BPF_LDX | BPF_MEMSX | BPF_B:
case BPF_LDX | BPF_MEMSX | BPF_H:
case BPF_LDX | BPF_MEMSX | BPF_W:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
{
int insn_len, insns_start;
bool sign_ext;
sign_ext = BPF_MODE(insn->code) == BPF_MEMSX ||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX;
switch (BPF_SIZE(code)) {
case BPF_B:
if (is_12b_int(off)) {
insns_start = ctx->ninsns;
emit(rv_lbu(rd, off, rs), ctx);
if (sign_ext)
emit(rv_lb(rd, off, rs), ctx);
else
emit(rv_lbu(rd, off, rs), ctx);
insn_len = ctx->ninsns - insns_start;
break;
}
@ -1499,15 +1556,19 @@ out_be:
emit_imm(RV_REG_T1, off, ctx);
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
insns_start = ctx->ninsns;
emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
if (sign_ext)
emit(rv_lb(rd, 0, RV_REG_T1), ctx);
else
emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
insn_len = ctx->ninsns - insns_start;
if (insn_is_zext(&insn[1]))
return 1;
break;
case BPF_H:
if (is_12b_int(off)) {
insns_start = ctx->ninsns;
emit(rv_lhu(rd, off, rs), ctx);
if (sign_ext)
emit(rv_lh(rd, off, rs), ctx);
else
emit(rv_lhu(rd, off, rs), ctx);
insn_len = ctx->ninsns - insns_start;
break;
}
@ -1515,15 +1576,19 @@ out_be:
emit_imm(RV_REG_T1, off, ctx);
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
insns_start = ctx->ninsns;
emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
if (sign_ext)
emit(rv_lh(rd, 0, RV_REG_T1), ctx);
else
emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
insn_len = ctx->ninsns - insns_start;
if (insn_is_zext(&insn[1]))
return 1;
break;
case BPF_W:
if (is_12b_int(off)) {
insns_start = ctx->ninsns;
emit(rv_lwu(rd, off, rs), ctx);
if (sign_ext)
emit(rv_lw(rd, off, rs), ctx);
else
emit(rv_lwu(rd, off, rs), ctx);
insn_len = ctx->ninsns - insns_start;
break;
}
@ -1531,10 +1596,11 @@ out_be:
emit_imm(RV_REG_T1, off, ctx);
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
insns_start = ctx->ninsns;
emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
if (sign_ext)
emit(rv_lw(rd, 0, RV_REG_T1), ctx);
else
emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
insn_len = ctx->ninsns - insns_start;
if (insn_is_zext(&insn[1]))
return 1;
break;
case BPF_DW:
if (is_12b_int(off)) {
@ -1555,6 +1621,9 @@ out_be:
ret = add_exception_handler(insn, ctx, rd, insn_len);
if (ret)
return ret;
if (BPF_SIZE(code) != BPF_DW && insn_is_zext(&insn[1]))
return 1;
break;
}
/* speculation barrier */
@ -1691,8 +1760,8 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx)
store_offset = stack_adjust - 8;
/* reserve 4 nop insns */
for (i = 0; i < 4; i++)
/* nops reserved for auipc+jalr pair */
for (i = 0; i < RV_FENTRY_NINSNS; i++)
emit(rv_nop(), ctx);
/* First instruction is always setting the tail-call-counter

View File

@ -701,6 +701,38 @@ static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
*pprog = prog;
}
static void emit_movsx_reg(u8 **pprog, int num_bits, bool is64, u32 dst_reg,
u32 src_reg)
{
u8 *prog = *pprog;
if (is64) {
/* movs[b,w,l]q dst, src */
if (num_bits == 8)
EMIT4(add_2mod(0x48, src_reg, dst_reg), 0x0f, 0xbe,
add_2reg(0xC0, src_reg, dst_reg));
else if (num_bits == 16)
EMIT4(add_2mod(0x48, src_reg, dst_reg), 0x0f, 0xbf,
add_2reg(0xC0, src_reg, dst_reg));
else if (num_bits == 32)
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x63,
add_2reg(0xC0, src_reg, dst_reg));
} else {
/* movs[b,w]l dst, src */
if (num_bits == 8) {
EMIT4(add_2mod(0x40, src_reg, dst_reg), 0x0f, 0xbe,
add_2reg(0xC0, src_reg, dst_reg));
} else if (num_bits == 16) {
if (is_ereg(dst_reg) || is_ereg(src_reg))
EMIT1(add_2mod(0x40, src_reg, dst_reg));
EMIT3(add_2mod(0x0f, src_reg, dst_reg), 0xbf,
add_2reg(0xC0, src_reg, dst_reg));
}
}
*pprog = prog;
}
/* Emit the suffix (ModR/M etc) for addressing *(ptr_reg + off) and val_reg */
static void emit_insn_suffix(u8 **pprog, u32 ptr_reg, u32 val_reg, int off)
{
@ -779,6 +811,29 @@ static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
*pprog = prog;
}
/* LDSX: dst_reg = *(s8*)(src_reg + off) */
static void emit_ldsx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
{
u8 *prog = *pprog;
switch (size) {
case BPF_B:
/* Emit 'movsx rax, byte ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xBE);
break;
case BPF_H:
/* Emit 'movsx rax, word ptr [rax + off]' */
EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xBF);
break;
case BPF_W:
/* Emit 'movsx rax, dword ptr [rax+0x14]' */
EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x63);
break;
}
emit_insn_suffix(&prog, 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)
{
@ -1028,9 +1083,14 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
case BPF_ALU64 | BPF_MOV | BPF_X:
case BPF_ALU | BPF_MOV | BPF_X:
emit_mov_reg(&prog,
BPF_CLASS(insn->code) == BPF_ALU64,
dst_reg, src_reg);
if (insn->off == 0)
emit_mov_reg(&prog,
BPF_CLASS(insn->code) == BPF_ALU64,
dst_reg, src_reg);
else
emit_movsx_reg(&prog, insn->off,
BPF_CLASS(insn->code) == BPF_ALU64,
dst_reg, src_reg);
break;
/* neg dst */
@ -1134,15 +1194,26 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
/* mov rax, dst_reg */
emit_mov_reg(&prog, is64, BPF_REG_0, dst_reg);
/*
* xor edx, edx
* equivalent to 'xor rdx, rdx', but one byte less
*/
EMIT2(0x31, 0xd2);
if (insn->off == 0) {
/*
* xor edx, edx
* equivalent to 'xor rdx, rdx', but one byte less
*/
EMIT2(0x31, 0xd2);
/* div src_reg */
maybe_emit_1mod(&prog, src_reg, is64);
EMIT2(0xF7, add_1reg(0xF0, src_reg));
/* div src_reg */
maybe_emit_1mod(&prog, src_reg, is64);
EMIT2(0xF7, add_1reg(0xF0, src_reg));
} else {
if (BPF_CLASS(insn->code) == BPF_ALU)
EMIT1(0x99); /* cdq */
else
EMIT2(0x48, 0x99); /* cqo */
/* idiv src_reg */
maybe_emit_1mod(&prog, src_reg, is64);
EMIT2(0xF7, add_1reg(0xF8, src_reg));
}
if (BPF_OP(insn->code) == BPF_MOD &&
dst_reg != BPF_REG_3)
@ -1262,6 +1333,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
break;
case BPF_ALU | BPF_END | BPF_FROM_BE:
case BPF_ALU64 | BPF_END | BPF_FROM_LE:
switch (imm32) {
case 16:
/* Emit 'ror %ax, 8' to swap lower 2 bytes */
@ -1370,9 +1442,17 @@ st: if (is_imm8(insn->off))
case BPF_LDX | BPF_PROBE_MEM | BPF_W:
case BPF_LDX | BPF_MEM | BPF_DW:
case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
/* LDXS: dst_reg = *(s8*)(src_reg + off) */
case BPF_LDX | BPF_MEMSX | BPF_B:
case BPF_LDX | BPF_MEMSX | BPF_H:
case BPF_LDX | BPF_MEMSX | BPF_W:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_B:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_H:
case BPF_LDX | BPF_PROBE_MEMSX | BPF_W:
insn_off = insn->off;
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
if (BPF_MODE(insn->code) == BPF_PROBE_MEM ||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX) {
/* Conservatively check that src_reg + insn->off is a kernel address:
* src_reg + insn->off >= TASK_SIZE_MAX + PAGE_SIZE
* src_reg is used as scratch for src_reg += insn->off and restored
@ -1415,8 +1495,13 @@ st: if (is_imm8(insn->off))
start_of_ldx = prog;
end_of_jmp[-1] = start_of_ldx - end_of_jmp;
}
emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
if (BPF_MODE(insn->code) == BPF_PROBE_MEM) {
if (BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
BPF_MODE(insn->code) == BPF_MEMSX)
emit_ldsx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
else
emit_ldx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
if (BPF_MODE(insn->code) == BPF_PROBE_MEM ||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX) {
struct exception_table_entry *ex;
u8 *_insn = image + proglen + (start_of_ldx - temp);
s64 delta;
@ -1730,16 +1815,24 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
break;
case BPF_JMP | BPF_JA:
if (insn->off == -1)
/* -1 jmp instructions will always jump
* backwards two bytes. Explicitly handling
* this case avoids wasting too many passes
* when there are long sequences of replaced
* dead code.
*/
jmp_offset = -2;
else
jmp_offset = addrs[i + insn->off] - addrs[i];
case BPF_JMP32 | BPF_JA:
if (BPF_CLASS(insn->code) == BPF_JMP) {
if (insn->off == -1)
/* -1 jmp instructions will always jump
* backwards two bytes. Explicitly handling
* this case avoids wasting too many passes
* when there are long sequences of replaced
* dead code.
*/
jmp_offset = -2;
else
jmp_offset = addrs[i + insn->off] - addrs[i];
} else {
if (insn->imm == -1)
jmp_offset = -2;
else
jmp_offset = addrs[i + insn->imm] - addrs[i];
}
if (!jmp_offset) {
/*
@ -1857,59 +1950,177 @@ emit_jmp:
return proglen;
}
static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,
int stack_size)
static void clean_stack_garbage(const struct btf_func_model *m,
u8 **pprog, int nr_stack_slots,
int stack_size)
{
int i, j, arg_size;
bool next_same_struct = false;
int arg_size, off;
u8 *prog;
/* Generally speaking, the compiler will pass the arguments
* on-stack with "push" instruction, which will take 8-byte
* on the stack. In this case, there won't be garbage values
* while we copy the arguments from origin stack frame to current
* in BPF_DW.
*
* However, sometimes the compiler will only allocate 4-byte on
* the stack for the arguments. For now, this case will only
* happen if there is only one argument on-stack and its size
* not more than 4 byte. In this case, there will be garbage
* values on the upper 4-byte where we store the argument on
* current stack frame.
*
* arguments on origin stack:
*
* stack_arg_1(4-byte) xxx(4-byte)
*
* what we copy:
*
* stack_arg_1(8-byte): stack_arg_1(origin) xxx
*
* and the xxx is the garbage values which we should clean here.
*/
if (nr_stack_slots != 1)
return;
/* the size of the last argument */
arg_size = m->arg_size[m->nr_args - 1];
if (arg_size <= 4) {
off = -(stack_size - 4);
prog = *pprog;
/* mov DWORD PTR [rbp + off], 0 */
if (!is_imm8(off))
EMIT2_off32(0xC7, 0x85, off);
else
EMIT3(0xC7, 0x45, off);
EMIT(0, 4);
*pprog = prog;
}
}
/* get the count of the regs that are used to pass arguments */
static int get_nr_used_regs(const struct btf_func_model *m)
{
int i, arg_regs, nr_used_regs = 0;
for (i = 0; i < min_t(int, m->nr_args, MAX_BPF_FUNC_ARGS); i++) {
arg_regs = (m->arg_size[i] + 7) / 8;
if (nr_used_regs + arg_regs <= 6)
nr_used_regs += arg_regs;
if (nr_used_regs >= 6)
break;
}
return nr_used_regs;
}
static void save_args(const struct btf_func_model *m, u8 **prog,
int stack_size, bool for_call_origin)
{
int arg_regs, first_off = 0, nr_regs = 0, nr_stack_slots = 0;
int i, j;
/* 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, j = 0; i < min(nr_regs, 6); i++) {
/* The arg_size is at most 16 bytes, enforced by the verifier. */
arg_size = m->arg_size[j];
if (arg_size > 8) {
arg_size = 8;
next_same_struct = !next_same_struct;
for (i = 0; i < min_t(int, m->nr_args, MAX_BPF_FUNC_ARGS); i++) {
arg_regs = (m->arg_size[i] + 7) / 8;
/* According to the research of Yonghong, struct members
* should be all in register or all on the stack.
* Meanwhile, the compiler will pass the argument on regs
* if the remaining regs can hold the argument.
*
* Disorder of the args can happen. For example:
*
* struct foo_struct {
* long a;
* int b;
* };
* int foo(char, char, char, char, char, struct foo_struct,
* char);
*
* the arg1-5,arg7 will be passed by regs, and arg6 will
* by stack.
*/
if (nr_regs + arg_regs > 6) {
/* copy function arguments from origin stack frame
* into current stack frame.
*
* The starting address of the arguments on-stack
* is:
* rbp + 8(push rbp) +
* 8(return addr of origin call) +
* 8(return addr of the caller)
* which means: rbp + 24
*/
for (j = 0; j < arg_regs; j++) {
emit_ldx(prog, BPF_DW, BPF_REG_0, BPF_REG_FP,
nr_stack_slots * 8 + 0x18);
emit_stx(prog, BPF_DW, BPF_REG_FP, BPF_REG_0,
-stack_size);
if (!nr_stack_slots)
first_off = stack_size;
stack_size -= 8;
nr_stack_slots++;
}
} else {
/* Only copy the arguments on-stack to current
* 'stack_size' and ignore the regs, used to
* prepare the arguments on-stack for orign call.
*/
if (for_call_origin) {
nr_regs += arg_regs;
continue;
}
/* copy the arguments from regs into stack */
for (j = 0; j < arg_regs; j++) {
emit_stx(prog, BPF_DW, BPF_REG_FP,
nr_regs == 5 ? X86_REG_R9 : BPF_REG_1 + nr_regs,
-stack_size);
stack_size -= 8;
nr_regs++;
}
}
emit_stx(prog, bytes_to_bpf_size(arg_size),
BPF_REG_FP,
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
-(stack_size - i * 8));
j = next_same_struct ? j : j + 1;
}
clean_stack_garbage(m, prog, nr_stack_slots, first_off);
}
static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,
static void restore_regs(const struct btf_func_model *m, u8 **prog,
int stack_size)
{
int i, j, arg_size;
bool next_same_struct = false;
int i, j, arg_regs, nr_regs = 0;
/* 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]
*
* The logic here is similar to what we do in save_args()
*/
for (i = 0, j = 0; i < min(nr_regs, 6); i++) {
/* The arg_size is at most 16 bytes, enforced by the verifier. */
arg_size = m->arg_size[j];
if (arg_size > 8) {
arg_size = 8;
next_same_struct = !next_same_struct;
for (i = 0; i < min_t(int, m->nr_args, MAX_BPF_FUNC_ARGS); i++) {
arg_regs = (m->arg_size[i] + 7) / 8;
if (nr_regs + arg_regs <= 6) {
for (j = 0; j < arg_regs; j++) {
emit_ldx(prog, BPF_DW,
nr_regs == 5 ? X86_REG_R9 : BPF_REG_1 + nr_regs,
BPF_REG_FP,
-stack_size);
stack_size -= 8;
nr_regs++;
}
} else {
stack_size -= 8 * arg_regs;
}
emit_ldx(prog, bytes_to_bpf_size(arg_size),
i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
BPF_REG_FP,
-(stack_size - i * 8));
j = next_same_struct ? j : j + 1;
if (nr_regs >= 6)
break;
}
}
@ -1938,7 +2149,10 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
/* arg1: mov rdi, progs[i] */
emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p);
/* arg2: lea rsi, [rbp - ctx_cookie_off] */
EMIT4(0x48, 0x8D, 0x75, -run_ctx_off);
if (!is_imm8(-run_ctx_off))
EMIT3_off32(0x48, 0x8D, 0xB5, -run_ctx_off);
else
EMIT4(0x48, 0x8D, 0x75, -run_ctx_off);
if (emit_rsb_call(&prog, bpf_trampoline_enter(p), prog))
return -EINVAL;
@ -1954,7 +2168,10 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
emit_nops(&prog, 2);
/* arg1: lea rdi, [rbp - stack_size] */
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
if (!is_imm8(-stack_size))
EMIT3_off32(0x48, 0x8D, 0xBD, -stack_size);
else
EMIT4(0x48, 0x8D, 0x7D, -stack_size);
/* arg2: progs[i]->insnsi for interpreter */
if (!p->jited)
emit_mov_imm64(&prog, BPF_REG_2,
@ -1984,7 +2201,10 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
/* arg2: mov rsi, rbx <- start time in nsec */
emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6);
/* arg3: lea rdx, [rbp - run_ctx_off] */
EMIT4(0x48, 0x8D, 0x55, -run_ctx_off);
if (!is_imm8(-run_ctx_off))
EMIT3_off32(0x48, 0x8D, 0x95, -run_ctx_off);
else
EMIT4(0x48, 0x8D, 0x55, -run_ctx_off);
if (emit_rsb_call(&prog, bpf_trampoline_exit(p), prog))
return -EINVAL;
@ -2136,7 +2356,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
void *func_addr)
{
int i, ret, nr_regs = m->nr_args, stack_size = 0;
int regs_off, nregs_off, ip_off, run_ctx_off;
int regs_off, nregs_off, ip_off, run_ctx_off, arg_stack_off, rbx_off;
struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
@ -2150,8 +2370,10 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
nr_regs += (m->arg_size[i] + 7) / 8 - 1;
/* x86-64 supports up to 6 arguments. 7+ can be added in the future */
if (nr_regs > 6)
/* x86-64 supports up to MAX_BPF_FUNC_ARGS arguments. 1-6
* are passed through regs, the remains are through stack.
*/
if (nr_regs > MAX_BPF_FUNC_ARGS)
return -ENOTSUPP;
/* Generated trampoline stack layout:
@ -2170,7 +2392,14 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
*
* RBP - ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
*
* RBP - rbx_off [ rbx value ] always
*
* RBP - run_ctx_off [ bpf_tramp_run_ctx ]
*
* [ stack_argN ] BPF_TRAMP_F_CALL_ORIG
* [ ... ]
* [ stack_arg2 ]
* RBP - arg_stack_off [ stack_arg1 ]
*/
/* room for return value of orig_call or fentry prog */
@ -2190,9 +2419,26 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
ip_off = stack_size;
stack_size += 8;
rbx_off = stack_size;
stack_size += (sizeof(struct bpf_tramp_run_ctx) + 7) & ~0x7;
run_ctx_off = stack_size;
if (nr_regs > 6 && (flags & BPF_TRAMP_F_CALL_ORIG)) {
/* the space that used to pass arguments on-stack */
stack_size += (nr_regs - get_nr_used_regs(m)) * 8;
/* make sure the stack pointer is 16-byte aligned if we
* need pass arguments on stack, which means
* [stack_size + 8(rbp) + 8(rip) + 8(origin rip)]
* should be 16-byte aligned. Following code depend on
* that stack_size is already 8-byte aligned.
*/
stack_size += (stack_size % 16) ? 0 : 8;
}
arg_stack_off = stack_size;
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
/* skip patched call instruction and point orig_call to actual
* body of the kernel function.
@ -2212,8 +2458,14 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
x86_call_depth_emit_accounting(&prog, NULL);
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 */
if (!is_imm8(stack_size))
/* sub rsp, stack_size */
EMIT3_off32(0x48, 0x81, 0xEC, stack_size);
else
/* sub rsp, stack_size */
EMIT4(0x48, 0x83, 0xEC, stack_size);
/* mov QWORD PTR [rbp - rbx_off], rbx */
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_6, -rbx_off);
/* Store number of argument registers of the traced function:
* mov rax, nr_regs
@ -2231,7 +2483,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off);
}
save_regs(m, &prog, nr_regs, regs_off);
save_args(m, &prog, regs_off, false);
if (flags & BPF_TRAMP_F_CALL_ORIG) {
/* arg1: mov rdi, im */
@ -2261,7 +2513,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
}
if (flags & BPF_TRAMP_F_CALL_ORIG) {
restore_regs(m, &prog, nr_regs, regs_off);
restore_regs(m, &prog, regs_off);
save_args(m, &prog, arg_stack_off, true);
if (flags & BPF_TRAMP_F_ORIG_STACK) {
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8);
@ -2302,7 +2555,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
}
if (flags & BPF_TRAMP_F_RESTORE_REGS)
restore_regs(m, &prog, nr_regs, regs_off);
restore_regs(m, &prog, regs_off);
/* This needs to be done regardless. If there were fmod_ret programs,
* the return value is only updated on the stack and still needs to be
@ -2321,7 +2574,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
if (save_ret)
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
EMIT1(0x5B); /* pop rbx */
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off);
EMIT1(0xC9); /* leave */
if (flags & BPF_TRAMP_F_SKIP_FRAME)
/* skip our return address and return to parent */

View File

@ -159,7 +159,7 @@ static int drbd_msg_sprintf_info(struct sk_buff *skb, const char *fmt, ...)
static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
struct sk_buff *skb, struct genl_info *info, unsigned flags)
{
struct drbd_genlmsghdr *d_in = info->userhdr;
struct drbd_genlmsghdr *d_in = genl_info_userhdr(info);
const u8 cmd = info->genlhdr->cmd;
int err;
@ -1396,8 +1396,9 @@ static void drbd_suspend_al(struct drbd_device *device)
static bool should_set_defaults(struct genl_info *info)
{
unsigned flags = ((struct drbd_genlmsghdr*)info->userhdr)->flags;
return 0 != (flags & DRBD_GENL_F_SET_DEFAULTS);
struct drbd_genlmsghdr *dh = genl_info_userhdr(info);
return 0 != (dh->flags & DRBD_GENL_F_SET_DEFAULTS);
}
static unsigned int drbd_al_extents_max(struct drbd_backing_dev *bdev)
@ -4276,7 +4277,7 @@ static void device_to_info(struct device_info *info,
int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_config_context adm_ctx;
struct drbd_genlmsghdr *dh = info->userhdr;
struct drbd_genlmsghdr *dh = genl_info_userhdr(info);
enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(&adm_ctx, skb, info, DRBD_ADM_NEED_RESOURCE);

View File

@ -24,6 +24,7 @@
#define BDADDR_BCM20702A1 (&(bdaddr_t) {{0x00, 0x00, 0xa0, 0x02, 0x70, 0x20}})
#define BDADDR_BCM2076B1 (&(bdaddr_t) {{0x79, 0x56, 0x00, 0xa0, 0x76, 0x20}})
#define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}})
#define BDADDR_BCM43430A1 (&(bdaddr_t) {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}})
#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}})
@ -115,6 +116,9 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
*
* The address 43:43:A0:12:1F:AC indicates a BCM43430A0 controller
* with no configured address.
*
* The address AA:AA:AA:AA:AA:AA indicates a BCM43430A1 controller
* with no configured address.
*/
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM20702A1) ||
@ -124,6 +128,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
!bacmp(&bda->bdaddr, BDADDR_BCM4334B0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4345C5) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43430A1) ||
!bacmp(&bda->bdaddr, BDADDR_BCM43341B)) {
/* Try falling back to BDADDR EFI variable */
if (btbcm_set_bdaddr_from_efi(hdev) != 0) {

View File

@ -10,6 +10,7 @@
#include <linux/firmware.h>
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@ -27,6 +28,11 @@
#define BTINTEL_PPAG_NAME "PPAG"
enum {
DSM_SET_WDISABLE2_DELAY = 1,
DSM_SET_RESET_METHOD = 3,
};
/* structure to store the PPAG data read from ACPI table */
struct btintel_ppag {
u32 domain;
@ -49,6 +55,10 @@ static struct {
u32 fw_build_num;
} coredump_info;
static const guid_t btintel_guid_dsm =
GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233,
0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9);
int btintel_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
@ -470,6 +480,7 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
case 0x18: /* Slr */
case 0x19: /* Slr-F */
case 0x1b: /* Mgr */
case 0x1c: /* Gale Peak (GaP) */
break;
default:
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
@ -2390,7 +2401,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver
{
struct btintel_ppag ppag;
struct sk_buff *skb;
struct btintel_loc_aware_reg ppag_cmd;
struct hci_ppag_enable_cmd ppag_cmd;
acpi_handle handle;
/* PPAG is not supported if CRF is HrP2, Jfp2, JfP1 */
@ -2398,6 +2409,8 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver
case 0x504: /* Hrp2 */
case 0x202: /* Jfp2 */
case 0x201: /* Jfp1 */
bt_dev_dbg(hdev, "PPAG not supported for Intel CNVr (0x%3x)",
ver->cnvr_top & 0xFFF);
return;
}
@ -2423,27 +2436,142 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver
}
if (ppag.domain != 0x12) {
bt_dev_warn(hdev, "PPAG-BT: domain is not bluetooth");
bt_dev_dbg(hdev, "PPAG-BT: Bluetooth domain is disabled in ACPI firmware");
return;
}
/* PPAG mode, BIT0 = 0 Disabled, BIT0 = 1 Enabled */
if (!(ppag.mode & BIT(0))) {
bt_dev_dbg(hdev, "PPAG-BT: disabled");
/* PPAG mode
* BIT 0 : 0 Disabled in EU
* 1 Enabled in EU
* BIT 1 : 0 Disabled in China
* 1 Enabled in China
*/
if ((ppag.mode & 0x01) != BIT(0) && (ppag.mode & 0x02) != BIT(1)) {
bt_dev_dbg(hdev, "PPAG-BT: EU, China mode are disabled in CB/BIOS");
return;
}
ppag_cmd.mcc = cpu_to_le32(0);
ppag_cmd.sel = cpu_to_le32(0); /* 0 - Enable , 1 - Disable, 2 - Testing mode */
ppag_cmd.delta = cpu_to_le32(0);
skb = __hci_cmd_sync(hdev, 0xfe19, sizeof(ppag_cmd), &ppag_cmd, HCI_CMD_TIMEOUT);
ppag_cmd.ppag_enable_flags = cpu_to_le32(ppag.mode);
skb = __hci_cmd_sync(hdev, INTEL_OP_PPAG_CMD, sizeof(ppag_cmd), &ppag_cmd, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_warn(hdev, "Failed to send PPAG Enable (%ld)", PTR_ERR(skb));
return;
}
bt_dev_info(hdev, "PPAG-BT: Enabled (Mode %d)", ppag.mode);
kfree_skb(skb);
}
static int btintel_acpi_reset_method(struct hci_dev *hdev)
{
int ret = 0;
acpi_status status;
union acpi_object *p, *ref;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer);
if (ACPI_FAILURE(status)) {
bt_dev_err(hdev, "Failed to run _PRR method");
ret = -ENODEV;
return ret;
}
p = buffer.pointer;
if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) {
bt_dev_err(hdev, "Invalid arguments");
ret = -EINVAL;
goto exit_on_error;
}
ref = &p->package.elements[0];
if (ref->type != ACPI_TYPE_LOCAL_REFERENCE) {
bt_dev_err(hdev, "Invalid object type: 0x%x", ref->type);
ret = -EINVAL;
goto exit_on_error;
}
status = acpi_evaluate_object(ref->reference.handle, "_RST", NULL, NULL);
if (ACPI_FAILURE(status)) {
bt_dev_err(hdev, "Failed to run_RST method");
ret = -ENODEV;
goto exit_on_error;
}
exit_on_error:
kfree(buffer.pointer);
return ret;
}
static void btintel_set_dsm_reset_method(struct hci_dev *hdev,
struct intel_version_tlv *ver_tlv)
{
struct btintel_data *data = hci_get_priv(hdev);
acpi_handle handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
u8 reset_payload[4] = {0x01, 0x00, 0x01, 0x00};
union acpi_object *obj, argv4;
enum {
RESET_TYPE_WDISABLE2,
RESET_TYPE_VSEC
};
handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
if (!handle) {
bt_dev_dbg(hdev, "No support for bluetooth device in ACPI firmware");
return;
}
if (!acpi_has_method(handle, "_PRR")) {
bt_dev_err(hdev, "No support for _PRR ACPI method");
return;
}
switch (ver_tlv->cnvi_top & 0xfff) {
case 0x910: /* GalePeak2 */
reset_payload[2] = RESET_TYPE_VSEC;
break;
default:
/* WDISABLE2 is the default reset method */
reset_payload[2] = RESET_TYPE_WDISABLE2;
if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
BIT(DSM_SET_WDISABLE2_DELAY))) {
bt_dev_err(hdev, "No dsm support to set reset delay");
return;
}
argv4.integer.type = ACPI_TYPE_INTEGER;
/* delay required to toggle BT power */
argv4.integer.value = 160;
obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
DSM_SET_WDISABLE2_DELAY, &argv4);
if (!obj) {
bt_dev_err(hdev, "Failed to call dsm to set reset delay");
return;
}
ACPI_FREE(obj);
}
bt_dev_info(hdev, "DSM reset method type: 0x%02x", reset_payload[2]);
if (!acpi_check_dsm(handle, &btintel_guid_dsm, 0,
DSM_SET_RESET_METHOD)) {
bt_dev_warn(hdev, "No support for dsm to set reset method");
return;
}
argv4.buffer.type = ACPI_TYPE_BUFFER;
argv4.buffer.length = sizeof(reset_payload);
argv4.buffer.pointer = reset_payload;
obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
DSM_SET_RESET_METHOD, &argv4);
if (!obj) {
bt_dev_err(hdev, "Failed to call dsm to set reset method");
return;
}
ACPI_FREE(obj);
data->acpi_reset_method = btintel_acpi_reset_method;
}
static int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
struct intel_version_tlv *ver)
{
@ -2528,6 +2656,7 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
case 0x18:
case 0x19:
case 0x1b:
case 0x1c:
hci_set_msft_opcode(hdev, 0xFC1E);
break;
default:
@ -2658,6 +2787,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
&hdev->quirks);
/* These variants don't seem to support LE Coded PHY */
set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks);
/* Setup MSFT Extension support */
btintel_set_msft_opcode(hdev, ver.hw_variant);
@ -2729,6 +2861,9 @@ static int btintel_setup_combined(struct hci_dev *hdev)
*/
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
/* These variants don't seem to support LE Coded PHY */
set_bit(HCI_QUIRK_BROKEN_LE_CODED, &hdev->quirks);
/* Set Valid LE States quirk */
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
@ -2742,6 +2877,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
case 0x18:
case 0x19:
case 0x1b:
case 0x1c:
/* Display version information of TLV type */
btintel_version_info_tlv(hdev, &ver_tlv);
@ -2757,6 +2893,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
/* Setup MSFT Extension support */
btintel_set_msft_opcode(hdev,
INTEL_HW_VARIANT(ver_tlv.cnvi_bt));
btintel_set_dsm_reset_method(hdev, &ver_tlv);
err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
btintel_register_devcoredump_support(hdev);
@ -2824,6 +2961,80 @@ int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
}
EXPORT_SYMBOL_GPL(btintel_configure_setup);
static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
struct intel_tlv *tlv = (void *)&skb->data[5];
/* The first event is always an event type TLV */
if (tlv->type != INTEL_TLV_TYPE_ID)
goto recv_frame;
switch (tlv->val[0]) {
case INTEL_TLV_SYSTEM_EXCEPTION:
case INTEL_TLV_FATAL_EXCEPTION:
case INTEL_TLV_DEBUG_EXCEPTION:
case INTEL_TLV_TEST_EXCEPTION:
/* Generate devcoredump from exception */
if (!hci_devcd_init(hdev, skb->len)) {
hci_devcd_append(hdev, skb);
hci_devcd_complete(hdev);
} else {
bt_dev_err(hdev, "Failed to generate devcoredump");
kfree_skb(skb);
}
return 0;
default:
bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
}
recv_frame:
return hci_recv_frame(hdev, skb);
}
int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *)skb->data;
const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
hdr->plen > 0) {
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
switch (skb->data[2]) {
case 0x02:
/* When switching to the operational firmware
* the device sends a vendor specific event
* indicating that the bootup completed.
*/
btintel_bootup(hdev, ptr, len);
break;
case 0x06:
/* When the firmware loading completes the
* device sends out a vendor specific event
* indicating the result of the firmware
* loading.
*/
btintel_secure_send_result(hdev, ptr, len);
break;
}
}
/* Handle all diagnostics events separately. May still call
* hci_recv_frame.
*/
if (len >= sizeof(diagnostics_hdr) &&
memcmp(&skb->data[2], diagnostics_hdr,
sizeof(diagnostics_hdr)) == 0) {
return btintel_diagnostics(hdev, skb);
}
}
return hci_recv_frame(hdev, skb);
}
EXPORT_SYMBOL_GPL(btintel_recv_event);
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len)
{
const struct intel_bootup *evt = ptr;

View File

@ -137,10 +137,9 @@ struct intel_offload_use_cases {
__u8 preset[8];
} __packed;
struct btintel_loc_aware_reg {
__le32 mcc;
__le32 sel;
__le32 delta;
#define INTEL_OP_PPAG_CMD 0xFE0B
struct hci_ppag_enable_cmd {
__le32 ppag_enable_flags;
} __packed;
#define INTEL_TLV_TYPE_ID 0x01
@ -166,12 +165,14 @@ enum {
INTEL_BROKEN_SHUTDOWN_LED,
INTEL_ROM_LEGACY,
INTEL_ROM_LEGACY_NO_WBS_SUPPORT,
INTEL_ACPI_RESET_ACTIVE,
__INTEL_NUM_FLAGS,
};
struct btintel_data {
DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
int (*acpi_reset_method)(struct hci_dev *hdev);
};
#define btintel_set_flag(hdev, nr) \
@ -220,6 +221,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
const struct firmware *fw, u32 *boot_param);
int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name);
int btintel_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
void btintel_secure_send_result(struct hci_dev *hdev,
const void *ptr, unsigned int len);

View File

@ -53,10 +53,61 @@ struct btmtk_section_map {
};
} __packed;
static void btmtk_coredump(struct hci_dev *hdev)
{
int err;
err = __hci_cmd_send(hdev, 0xfd5b, 0, NULL);
if (err < 0)
bt_dev_err(hdev, "Coredump failed (%d)", err);
}
static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmediatek_data *data = hci_get_priv(hdev);
char buf[80];
snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
data->dev_id);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
data->cd_info.fw_version);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Driver: %s\n",
data->cd_info.driver_name);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Vendor: MediaTek\n");
skb_put_data(skb, buf, strlen(buf));
}
static void btmtk_coredump_notify(struct hci_dev *hdev, int state)
{
struct btmediatek_data *data = hci_get_priv(hdev);
switch (state) {
case HCI_DEVCOREDUMP_IDLE:
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
break;
case HCI_DEVCOREDUMP_ACTIVE:
data->cd_info.state = HCI_DEVCOREDUMP_ACTIVE;
break;
case HCI_DEVCOREDUMP_TIMEOUT:
case HCI_DEVCOREDUMP_ABORT:
case HCI_DEVCOREDUMP_DONE:
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
btmtk_reset_sync(hdev);
break;
}
}
int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync)
{
struct btmtk_hci_wmt_params wmt_params;
struct btmtk_patch_header *hdr;
struct btmtk_global_desc *globaldesc = NULL;
struct btmtk_section_map *sectionmap;
const struct firmware *fw;
@ -75,9 +126,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
fw_ptr = fw->data;
fw_bin_ptr = fw_ptr;
hdr = (struct btmtk_patch_header *)fw_ptr;
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
section_num = le32_to_cpu(globaldesc->section_num);
bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
for (i = 0; i < section_num; i++) {
first_block = 1;
fw_ptr = fw_bin_ptr;
@ -280,6 +335,83 @@ int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(btmtk_set_bdaddr);
void btmtk_reset_sync(struct hci_dev *hdev)
{
struct btmediatek_data *reset_work = hci_get_priv(hdev);
int err;
hci_dev_lock(hdev);
err = hci_cmd_sync_queue(hdev, reset_work->reset_sync, NULL, NULL);
if (err)
bt_dev_err(hdev, "failed to reset (%d)", err);
hci_dev_unlock(hdev);
}
EXPORT_SYMBOL_GPL(btmtk_reset_sync);
int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
u32 fw_version)
{
struct btmediatek_data *data = hci_get_priv(hdev);
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
return -EOPNOTSUPP;
data->cd_info.fw_version = fw_version;
data->cd_info.state = HCI_DEVCOREDUMP_IDLE;
data->cd_info.driver_name = name;
return hci_devcd_register(hdev, btmtk_coredump, btmtk_coredump_hdr,
btmtk_coredump_notify);
}
EXPORT_SYMBOL_GPL(btmtk_register_coredump);
int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmediatek_data *data = hci_get_priv(hdev);
int err;
if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
return 0;
switch (data->cd_info.state) {
case HCI_DEVCOREDUMP_IDLE:
err = hci_devcd_init(hdev, MTK_COREDUMP_SIZE);
if (err < 0)
break;
data->cd_info.cnt = 0;
/* It is supposed coredump can be done within 5 seconds */
schedule_delayed_work(&hdev->dump.dump_timeout,
msecs_to_jiffies(5000));
fallthrough;
case HCI_DEVCOREDUMP_ACTIVE:
default:
err = hci_devcd_append(hdev, skb);
if (err < 0)
break;
data->cd_info.cnt++;
/* Mediatek coredump data would be more than MTK_COREDUMP_NUM */
if (data->cd_info.cnt > MTK_COREDUMP_NUM &&
skb->len > MTK_COREDUMP_END_LEN)
if (!memcmp((char *)&skb->data[skb->len - MTK_COREDUMP_END_LEN],
MTK_COREDUMP_END, MTK_COREDUMP_END_LEN - 1)) {
bt_dev_info(hdev, "Mediatek coredump end");
hci_devcd_complete(hdev);
}
break;
}
if (err < 0)
kfree_skb(skb);
return err;
}
EXPORT_SYMBOL_GPL(btmtk_process_coredump);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
@ -289,3 +421,4 @@ MODULE_FIRMWARE(FIRMWARE_MT7622);
MODULE_FIRMWARE(FIRMWARE_MT7663);
MODULE_FIRMWARE(FIRMWARE_MT7668);
MODULE_FIRMWARE(FIRMWARE_MT7961);
MODULE_FIRMWARE(FIRMWARE_MT7925);

View File

@ -5,6 +5,7 @@
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
#define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
#define HCI_EV_WMT 0xe4
#define HCI_WMT_MAX_EVENT_SIZE 64
@ -21,6 +22,11 @@
#define MT7921_DLSTATUS 0x7c053c10
#define BT_DL_STATE BIT(1)
#define MTK_COREDUMP_SIZE (1024 * 1000)
#define MTK_COREDUMP_END "coredump end"
#define MTK_COREDUMP_END_LEN (sizeof(MTK_COREDUMP_END))
#define MTK_COREDUMP_NUM 255
enum {
BTMTK_WMT_PATCH_DWNLD = 0x1,
BTMTK_WMT_TEST = 0x2,
@ -119,6 +125,21 @@ struct btmtk_hci_wmt_params {
u32 *status;
};
typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
struct btmtk_coredump_info {
const char *driver_name;
u32 fw_version;
u16 cnt;
int state;
};
struct btmediatek_data {
u32 dev_id;
btmtk_reset_sync_func_t reset_sync;
struct btmtk_coredump_info cd_info;
};
typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *,
struct btmtk_hci_wmt_params *);
@ -131,6 +152,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync);
void btmtk_reset_sync(struct hci_dev *hdev);
int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
u32 fw_version);
int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
#else
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
@ -151,4 +179,18 @@ static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
return -EOPNOTSUPP;
}
static void btmtk_reset_sync(struct hci_dev *hdev)
{
}
static int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
u32 fw_version)
{
return -EOPNOTSUPP;
}
static int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
#endif

View File

@ -17,7 +17,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>

View File

@ -28,17 +28,34 @@
#define BTNXPUART_FW_DOWNLOADING 2
#define BTNXPUART_CHECK_BOOT_SIGNATURE 3
#define BTNXPUART_SERDEV_OPEN 4
#define BTNXPUART_IR_IN_PROGRESS 5
#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin"
#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin"
#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin"
#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin"
#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se"
#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin"
/* NXP HW err codes */
#define BTNXPUART_IR_HW_ERR 0xb0
#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin"
#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin"
#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin"
#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin"
#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se"
#define FIRMWARE_IW624 "nxp/uartiw624_bt.bin"
#define FIRMWARE_SECURE_IW624 "nxp/uartiw624_bt.bin.se"
#define FIRMWARE_AW693 "nxp/uartaw693_bt.bin"
#define FIRMWARE_SECURE_AW693 "nxp/uartaw693_bt.bin.se"
#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin"
#define CHIP_ID_W9098 0x5c03
#define CHIP_ID_IW416 0x7201
#define CHIP_ID_IW612 0x7601
#define CHIP_ID_IW624a 0x8000
#define CHIP_ID_IW624c 0x8001
#define CHIP_ID_AW693 0x8200
#define FW_SECURE_MASK 0xc0
#define FW_OPEN 0x00
#define FW_AUTH_ILLEGAL 0x40
#define FW_AUTH_PLAIN 0x80
#define FW_AUTH_ENC 0xc0
#define HCI_NXP_PRI_BAUDRATE 115200
#define HCI_NXP_SEC_BAUDRATE 3000000
@ -143,6 +160,7 @@ struct btnxpuart_dev {
u32 fw_v1_sent_bytes;
u32 fw_v3_offset_correction;
u32 fw_v1_expected_len;
u32 boot_reg_offset;
wait_queue_head_t fw_dnld_done_wait_q;
wait_queue_head_t check_boot_sign_wait_q;
@ -366,39 +384,13 @@ static void ps_timeout_func(struct timer_list *t)
}
}
static int ps_init_work(struct hci_dev *hdev)
static void ps_setup(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct ps_data *psdata = &nxpdev->psdata;
psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
psdata->ps_state = PS_STATE_AWAKE;
psdata->target_ps_mode = DEFAULT_PS_MODE;
psdata->hdev = hdev;
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
psdata->c2h_wakeup_gpio = 0xff;
switch (DEFAULT_H2C_WAKEUP_MODE) {
case WAKEUP_METHOD_DTR:
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
break;
case WAKEUP_METHOD_BREAK:
default:
psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
break;
}
psdata->cur_psmode = PS_MODE_DISABLE;
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
INIT_WORK(&psdata->work, ps_work_func);
return 0;
}
static void ps_init_timer(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct ps_data *psdata = &nxpdev->psdata;
timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
}
@ -501,19 +493,31 @@ static void ps_init(struct hci_dev *hdev)
serdev_device_set_tiocm(nxpdev->serdev, TIOCM_RTS, 0);
usleep_range(5000, 10000);
switch (psdata->h2c_wakeupmode) {
psdata->ps_state = PS_STATE_AWAKE;
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
psdata->c2h_wakeup_gpio = 0xff;
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
switch (DEFAULT_H2C_WAKEUP_MODE) {
case WAKEUP_METHOD_DTR:
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR);
serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
break;
case WAKEUP_METHOD_BREAK:
default:
psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
serdev_device_break_ctl(nxpdev->serdev, -1);
usleep_range(5000, 10000);
serdev_device_break_ctl(nxpdev->serdev, 0);
usleep_range(5000, 10000);
break;
}
psdata->cur_psmode = PS_MODE_DISABLE;
psdata->target_ps_mode = DEFAULT_PS_MODE;
if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode)
hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL);
if (psdata->cur_psmode != psdata->target_ps_mode)
@ -529,6 +533,7 @@ static int nxp_download_firmware(struct hci_dev *hdev)
nxpdev->fw_dnld_v1_offset = 0;
nxpdev->fw_v1_sent_bytes = 0;
nxpdev->fw_v1_expected_len = HDR_LEN;
nxpdev->boot_reg_offset = 0;
nxpdev->fw_v3_offset_correction = 0;
nxpdev->baudrate_changed = false;
nxpdev->timeout_changed = false;
@ -538,7 +543,7 @@ static int nxp_download_firmware(struct hci_dev *hdev)
serdev_device_set_flow_control(nxpdev->serdev, false);
nxpdev->current_baudrate = HCI_NXP_PRI_BAUDRATE;
/* Wait till FW is downloaded and CTS becomes low */
/* Wait till FW is downloaded */
err = wait_event_interruptible_timeout(nxpdev->fw_dnld_done_wait_q,
!test_bit(BTNXPUART_FW_DOWNLOADING,
&nxpdev->tx_state),
@ -549,16 +554,11 @@ static int nxp_download_firmware(struct hci_dev *hdev)
}
serdev_device_set_flow_control(nxpdev->serdev, true);
err = serdev_device_wait_for_cts(nxpdev->serdev, 1, 60000);
if (err < 0) {
bt_dev_err(hdev, "CTS is still high. FW Download failed.");
return err;
}
release_firmware(nxpdev->fw);
memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
/* Allow the downloaded FW to initialize */
usleep_range(800 * USEC_PER_MSEC, 1 * USEC_PER_SEC);
msleep(1200);
return 0;
}
@ -582,6 +582,12 @@ static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len)
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct nxp_bootloader_cmd nxp_cmd5;
struct uart_config uart_config;
u32 clkdivaddr = CLKDIVADDR - nxpdev->boot_reg_offset;
u32 uartdivaddr = UARTDIVADDR - nxpdev->boot_reg_offset;
u32 uartmcraddr = UARTMCRADDR - nxpdev->boot_reg_offset;
u32 uartreinitaddr = UARTREINITADDR - nxpdev->boot_reg_offset;
u32 uarticraddr = UARTICRADDR - nxpdev->boot_reg_offset;
u32 uartfcraddr = UARTFCRADDR - nxpdev->boot_reg_offset;
if (req_len == sizeof(nxp_cmd5)) {
nxp_cmd5.header = __cpu_to_le32(5);
@ -594,17 +600,17 @@ static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len)
serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd5, sizeof(nxp_cmd5));
nxpdev->fw_v3_offset_correction += req_len;
} else if (req_len == sizeof(uart_config)) {
uart_config.clkdiv.address = __cpu_to_le32(CLKDIVADDR);
uart_config.clkdiv.address = __cpu_to_le32(clkdivaddr);
uart_config.clkdiv.value = __cpu_to_le32(0x00c00000);
uart_config.uartdiv.address = __cpu_to_le32(UARTDIVADDR);
uart_config.uartdiv.address = __cpu_to_le32(uartdivaddr);
uart_config.uartdiv.value = __cpu_to_le32(1);
uart_config.mcr.address = __cpu_to_le32(UARTMCRADDR);
uart_config.mcr.address = __cpu_to_le32(uartmcraddr);
uart_config.mcr.value = __cpu_to_le32(MCR);
uart_config.re_init.address = __cpu_to_le32(UARTREINITADDR);
uart_config.re_init.address = __cpu_to_le32(uartreinitaddr);
uart_config.re_init.value = __cpu_to_le32(INIT);
uart_config.icr.address = __cpu_to_le32(UARTICRADDR);
uart_config.icr.address = __cpu_to_le32(uarticraddr);
uart_config.icr.value = __cpu_to_le32(ICR);
uart_config.fcr.address = __cpu_to_le32(UARTFCRADDR);
uart_config.fcr.address = __cpu_to_le32(uartfcraddr);
uart_config.fcr.value = __cpu_to_le32(FCR);
/* FW expects swapped CRC bytes */
uart_config.crc = __cpu_to_be32(crc32_be(0UL, (char *)&uart_config,
@ -665,6 +671,9 @@ static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name)
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
int err = 0;
if (!fw_name)
return -ENOENT;
if (!strlen(nxpdev->fw_name)) {
snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "%s", fw_name);
@ -690,7 +699,7 @@ static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb)
goto free_skb;
chip_id = le16_to_cpu(req->chip_id ^ req->chip_id_comp);
if (chip_id == 0xffff) {
if (chip_id == 0xffff && nxpdev->fw_dnld_v1_offset) {
nxpdev->fw_dnld_v1_offset = 0;
nxpdev->fw_v1_sent_bytes = 0;
nxpdev->fw_v1_expected_len = HDR_LEN;
@ -812,8 +821,10 @@ free_skb:
return 0;
}
static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid)
static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid,
u8 loader_ver)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
char *fw_name = NULL;
switch (chipid) {
@ -826,6 +837,24 @@ static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid)
case CHIP_ID_IW612:
fw_name = FIRMWARE_IW612;
break;
case CHIP_ID_IW624a:
case CHIP_ID_IW624c:
nxpdev->boot_reg_offset = 1;
if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
fw_name = FIRMWARE_IW624;
else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
fw_name = FIRMWARE_SECURE_IW624;
else
bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
break;
case CHIP_ID_AW693:
if ((loader_ver & FW_SECURE_MASK) == FW_OPEN)
fw_name = FIRMWARE_AW693;
else if ((loader_ver & FW_SECURE_MASK) != FW_AUTH_ILLEGAL)
fw_name = FIRMWARE_SECURE_AW693;
else
bt_dev_err(hdev, "Illegal loader version %02x", loader_ver);
break;
default:
bt_dev_err(hdev, "Unknown chip signature %04x", chipid);
break;
@ -838,13 +867,15 @@ static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb)
struct v3_start_ind *req = skb_pull_data(skb, sizeof(*req));
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
u16 chip_id;
u8 loader_ver;
if (!process_boot_signature(nxpdev))
goto free_skb;
chip_id = le16_to_cpu(req->chip_id);
loader_ver = req->loader_ver;
if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev,
chip_id)))
chip_id, loader_ver)))
nxp_send_ack(NXP_ACK_V3, hdev);
free_skb:
@ -946,45 +977,13 @@ static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data)
return 0;
}
static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct sk_buff *skb;
u8 *status;
u8 pcmd = 0;
int err = 0;
skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd);
if (IS_ERR(skb))
return PTR_ERR(skb);
status = skb_pull_data(skb, 1);
if (!status || *status)
goto free_skb;
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
err = nxp_download_firmware(hdev);
if (err < 0)
goto free_skb;
serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate);
nxpdev->current_baudrate = nxpdev->fw_init_baudrate;
if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) {
nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE;
nxp_set_baudrate_cmd(hdev, NULL);
}
hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL);
hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL);
free_skb:
kfree_skb(skb);
return err;
}
/* NXP protocol */
static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
{
serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
serdev_device_set_flow_control(nxpdev->serdev, true);
if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
serdev_device_set_flow_control(nxpdev->serdev, false);
else
serdev_device_set_flow_control(nxpdev->serdev, true);
set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state);
return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q,
@ -993,15 +992,29 @@ static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
msecs_to_jiffies(1000));
}
static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
{
static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR,
0x01, BTNXPUART_IR_HW_ERR };
struct sk_buff *skb;
skb = bt_skb_alloc(3, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
skb_put_data(skb, ir_hw_err, 3);
/* Inject Hardware Error to upper stack */
return hci_recv_frame(hdev, skb);
}
/* NXP protocol */
static int nxp_setup(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
int err = 0;
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
if (nxp_check_boot_sign(nxpdev)) {
bt_dev_dbg(hdev, "Need FW Download.");
err = nxp_download_firmware(hdev);
@ -1012,10 +1025,6 @@ static int nxp_setup(struct hci_dev *hdev)
clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
}
device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
&nxpdev->fw_init_baudrate);
if (!nxpdev->fw_init_baudrate)
nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate);
nxpdev->current_baudrate = nxpdev->fw_init_baudrate;
@ -1026,6 +1035,46 @@ static int nxp_setup(struct hci_dev *hdev)
ps_init(hdev);
if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
hci_dev_clear_flag(hdev, HCI_SETUP);
return 0;
}
static void nxp_hw_err(struct hci_dev *hdev, u8 code)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
switch (code) {
case BTNXPUART_IR_HW_ERR:
set_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state);
hci_dev_set_flag(hdev, HCI_SETUP);
break;
default:
break;
}
}
static int nxp_shutdown(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct sk_buff *skb;
u8 *status;
u8 pcmd = 0;
if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) {
skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd);
if (IS_ERR(skb))
return PTR_ERR(skb);
status = skb_pull_data(skb, 1);
if (status) {
serdev_device_set_flow_control(nxpdev->serdev, false);
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
}
kfree_skb(skb);
}
return 0;
}
@ -1233,7 +1282,8 @@ static int btnxpuart_receive_buf(struct serdev_device *serdev, const u8 *data,
nxpdev->rx_skb = NULL;
return err;
}
nxpdev->hdev->stat.byte_rx += count;
if (!is_fw_downloading(nxpdev))
nxpdev->hdev->stat.byte_rx += count;
return count;
}
@ -1266,6 +1316,16 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
INIT_WORK(&nxpdev->tx_work, btnxpuart_tx_work);
skb_queue_head_init(&nxpdev->txq);
init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
&nxpdev->fw_init_baudrate);
if (!nxpdev->fw_init_baudrate)
nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
crc8_populate_msb(crc8_table, POLYNOMIAL8);
/* Initialize and register HCI device */
@ -1286,6 +1346,8 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
hdev->flush = btnxpuart_flush;
hdev->setup = nxp_setup;
hdev->send = nxp_enqueue;
hdev->hw_error = nxp_hw_err;
hdev->shutdown = nxp_shutdown;
SET_HCIDEV_DEV(hdev, &serdev->dev);
if (hci_register_dev(hdev) < 0) {
@ -1294,8 +1356,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
return -ENODEV;
}
ps_init_work(hdev);
ps_init_timer(hdev);
ps_setup(hdev);
return 0;
}

View File

@ -594,30 +594,48 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
/* Firmware files to download are based on ROM version.
* ROM version is derived from last two bytes of soc_ver.
*/
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
if (soc_type == QCA_WCN3988)
rom_ver = ((soc_ver & 0x00000f00) >> 0x05) | (soc_ver & 0x0000000f);
else
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
if (soc_type == QCA_WCN6750)
qca_send_patch_config_cmd(hdev);
/* Download rampatch file */
config.type = TLV_TYPE_PATCH;
if (qca_is_wcn399x(soc_type)) {
switch (soc_type) {
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
snprintf(config.fwname, sizeof(config.fwname),
"qca/crbtfw%02x.tlv", rom_ver);
} else if (soc_type == QCA_QCA6390) {
break;
case QCA_WCN3988:
snprintf(config.fwname, sizeof(config.fwname),
"qca/apbtfw%02x.tlv", rom_ver);
break;
case QCA_QCA6390:
snprintf(config.fwname, sizeof(config.fwname),
"qca/htbtfw%02x.tlv", rom_ver);
} else if (soc_type == QCA_WCN6750) {
break;
case QCA_WCN6750:
/* Choose mbn file by default.If mbn file is not found
* then choose tlv file
*/
config.type = ELF_TYPE_PATCH;
snprintf(config.fwname, sizeof(config.fwname),
"qca/msbtfw%02x.mbn", rom_ver);
} else if (soc_type == QCA_WCN6855) {
break;
case QCA_WCN6855:
snprintf(config.fwname, sizeof(config.fwname),
"qca/hpbtfw%02x.tlv", rom_ver);
} else {
break;
case QCA_WCN7850:
snprintf(config.fwname, sizeof(config.fwname),
"qca/hmtbtfw%02x.tlv", rom_ver);
break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/rampatch_%08x.bin", soc_ver);
}
@ -633,30 +651,48 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
if (firmware_name)
if (firmware_name) {
snprintf(config.fwname, sizeof(config.fwname),
"qca/%s", firmware_name);
else if (qca_is_wcn399x(soc_type)) {
if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID) {
} else {
switch (soc_type) {
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
if (le32_to_cpu(ver.soc_id) == QCA_WCN3991_SOC_ID) {
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02xu.bin", rom_ver);
} else {
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02x.bin", rom_ver);
}
break;
case QCA_WCN3988:
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02xu.bin", rom_ver);
} else {
"qca/apnv%02x.bin", rom_ver);
break;
case QCA_QCA6390:
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02x.bin", rom_ver);
"qca/htnv%02x.bin", rom_ver);
break;
case QCA_WCN6750:
snprintf(config.fwname, sizeof(config.fwname),
"qca/msnv%02x.bin", rom_ver);
break;
case QCA_WCN6855:
snprintf(config.fwname, sizeof(config.fwname),
"qca/hpnv%02x.bin", rom_ver);
break;
case QCA_WCN7850:
snprintf(config.fwname, sizeof(config.fwname),
"qca/hmtnv%02x.bin", rom_ver);
break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
}
}
else if (soc_type == QCA_QCA6390)
snprintf(config.fwname, sizeof(config.fwname),
"qca/htnv%02x.bin", rom_ver);
else if (soc_type == QCA_WCN6750)
snprintf(config.fwname, sizeof(config.fwname),
"qca/msnv%02x.bin", rom_ver);
else if (soc_type == QCA_WCN6855)
snprintf(config.fwname, sizeof(config.fwname),
"qca/hpnv%02x.bin", rom_ver);
else
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
err = qca_download_firmware(hdev, &config, soc_type, rom_ver);
if (err < 0) {
@ -664,16 +700,25 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
if (soc_type >= QCA_WCN3991) {
switch (soc_type) {
case QCA_WCN3991:
case QCA_QCA6390:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
err = qca_disable_soc_logging(hdev);
if (err < 0)
return err;
break;
default:
break;
}
/* WCN399x and WCN6750 supports the Microsoft vendor extension with 0xFD70 as the
* VsMsftOpCode.
*/
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
@ -695,6 +740,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN3991:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)

View File

@ -142,12 +142,14 @@ enum qca_btsoc_type {
QCA_INVALID = -1,
QCA_AR3002,
QCA_ROME,
QCA_WCN3988,
QCA_WCN3990,
QCA_WCN3998,
QCA_WCN3991,
QCA_QCA6390,
QCA_WCN6750,
QCA_WCN6855,
QCA_WCN7850,
};
#if IS_ENABLED(CONFIG_BT_QCA)
@ -160,20 +162,6 @@ int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
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_WCN3991 ||
soc_type == QCA_WCN3998;
}
static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
{
return soc_type == QCA_WCN6750;
}
static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type)
{
return soc_type == QCA_WCN6855;
}
#else
static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
@ -201,21 +189,6 @@ static inline int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return -EOPNOTSUPP;
}
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
{
return false;
}
static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type)
{
return false;
}
static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type)
{
return false;
}
static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
{
return -EOPNOTSUPP;

View File

@ -32,6 +32,8 @@
#define RTL_ROM_LMP_8851B 0x8851
#define RTL_CONFIG_MAGIC 0x8723ab55
#define RTL_VSC_OP_COREDUMP 0xfcff
#define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1)
#define IC_MATCH_FL_HCIVER (1 << 2)
@ -81,6 +83,7 @@ struct id_table {
bool has_msft_ext;
char *fw_name;
char *cfg_name;
char *hw_info;
};
struct btrtl_device_info {
@ -101,22 +104,25 @@ static const struct id_table ic_id_table[] = {
{ IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = false,
.fw_name = "rtl_bt/rtl8723a_fw.bin",
.cfg_name = NULL },
.fw_name = "rtl_bt/rtl8723a_fw",
.cfg_name = NULL,
.hw_info = "rtl8723au" },
/* 8723BS */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723bs_fw.bin",
.cfg_name = "rtl_bt/rtl8723bs_config" },
.fw_name = "rtl_bt/rtl8723bs_fw",
.cfg_name = "rtl_bt/rtl8723bs_config",
.hw_info = "rtl8723bs" },
/* 8723B */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723b_fw.bin",
.cfg_name = "rtl_bt/rtl8723b_config" },
.fw_name = "rtl_bt/rtl8723b_fw",
.cfg_name = "rtl_bt/rtl8723b_config",
.hw_info = "rtl8723bu" },
/* 8723CS-CG */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
@ -126,8 +132,9 @@ static const struct id_table ic_id_table[] = {
.hci_bus = HCI_UART,
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723cs_cg_fw.bin",
.cfg_name = "rtl_bt/rtl8723cs_cg_config" },
.fw_name = "rtl_bt/rtl8723cs_cg_fw",
.cfg_name = "rtl_bt/rtl8723cs_cg_config",
.hw_info = "rtl8723cs-cg" },
/* 8723CS-VF */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
@ -137,8 +144,9 @@ static const struct id_table ic_id_table[] = {
.hci_bus = HCI_UART,
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723cs_vf_fw.bin",
.cfg_name = "rtl_bt/rtl8723cs_vf_config" },
.fw_name = "rtl_bt/rtl8723cs_vf_fw",
.cfg_name = "rtl_bt/rtl8723cs_vf_config",
.hw_info = "rtl8723cs-vf" },
/* 8723CS-XX */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE |
@ -148,139 +156,157 @@ static const struct id_table ic_id_table[] = {
.hci_bus = HCI_UART,
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723cs_xx_fw.bin",
.cfg_name = "rtl_bt/rtl8723cs_xx_config" },
.fw_name = "rtl_bt/rtl8723cs_xx_fw",
.cfg_name = "rtl_bt/rtl8723cs_xx_config",
.hw_info = "rtl8723cs" },
/* 8723D */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723d_fw.bin",
.cfg_name = "rtl_bt/rtl8723d_config" },
.fw_name = "rtl_bt/rtl8723d_fw",
.cfg_name = "rtl_bt/rtl8723d_config",
.hw_info = "rtl8723du" },
/* 8723DS */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
.cfg_name = "rtl_bt/rtl8723ds_config" },
.fw_name = "rtl_bt/rtl8723ds_fw",
.cfg_name = "rtl_bt/rtl8723ds_config",
.hw_info = "rtl8723ds" },
/* 8821A */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8821a_fw.bin",
.cfg_name = "rtl_bt/rtl8821a_config" },
.fw_name = "rtl_bt/rtl8821a_fw",
.cfg_name = "rtl_bt/rtl8821a_config",
.hw_info = "rtl8821au" },
/* 8821C */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8821c_fw.bin",
.cfg_name = "rtl_bt/rtl8821c_config" },
.fw_name = "rtl_bt/rtl8821c_fw",
.cfg_name = "rtl_bt/rtl8821c_config",
.hw_info = "rtl8821cu" },
/* 8821CS */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8821cs_fw.bin",
.cfg_name = "rtl_bt/rtl8821cs_config" },
.fw_name = "rtl_bt/rtl8821cs_fw",
.cfg_name = "rtl_bt/rtl8821cs_config",
.hw_info = "rtl8821cs" },
/* 8761A */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8761a_fw.bin",
.cfg_name = "rtl_bt/rtl8761a_config" },
.fw_name = "rtl_bt/rtl8761a_fw",
.cfg_name = "rtl_bt/rtl8761a_config",
.hw_info = "rtl8761au" },
/* 8761B */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8761b_fw.bin",
.cfg_name = "rtl_bt/rtl8761b_config" },
.fw_name = "rtl_bt/rtl8761b_fw",
.cfg_name = "rtl_bt/rtl8761b_config",
.hw_info = "rtl8761btv" },
/* 8761BU */
{ IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8761bu_fw.bin",
.cfg_name = "rtl_bt/rtl8761bu_config" },
.fw_name = "rtl_bt/rtl8761bu_fw",
.cfg_name = "rtl_bt/rtl8761bu_config",
.hw_info = "rtl8761bu" },
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
.cfg_name = "rtl_bt/rtl8822cs_config" },
.fw_name = "rtl_bt/rtl8822cs_fw",
.cfg_name = "rtl_bt/rtl8822cs_config",
.hw_info = "rtl8822cs" },
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822cs_fw.bin",
.cfg_name = "rtl_bt/rtl8822cs_config" },
.fw_name = "rtl_bt/rtl8822cs_fw",
.cfg_name = "rtl_bt/rtl8822cs_config",
.hw_info = "rtl8822cs" },
/* 8822C with USB interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822cu_fw.bin",
.cfg_name = "rtl_bt/rtl8822cu_config" },
.fw_name = "rtl_bt/rtl8822cu_fw",
.cfg_name = "rtl_bt/rtl8822cu_config",
.hw_info = "rtl8822cu" },
/* 8822B */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8822b_fw.bin",
.cfg_name = "rtl_bt/rtl8822b_config" },
.fw_name = "rtl_bt/rtl8822b_fw",
.cfg_name = "rtl_bt/rtl8822b_config",
.hw_info = "rtl8822bu" },
/* 8852A */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8852au_fw.bin",
.cfg_name = "rtl_bt/rtl8852au_config" },
.fw_name = "rtl_bt/rtl8852au_fw",
.cfg_name = "rtl_bt/rtl8852au_config",
.hw_info = "rtl8852au" },
/* 8852B with UART interface */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART),
.config_needed = true,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8852bs_fw.bin",
.cfg_name = "rtl_bt/rtl8852bs_config" },
.fw_name = "rtl_bt/rtl8852bs_fw",
.cfg_name = "rtl_bt/rtl8852bs_config",
.hw_info = "rtl8852bs" },
/* 8852B */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8852bu_fw.bin",
.cfg_name = "rtl_bt/rtl8852bu_config" },
.fw_name = "rtl_bt/rtl8852bu_fw",
.cfg_name = "rtl_bt/rtl8852bu_config",
.hw_info = "rtl8852bu" },
/* 8852C */
{ IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = true,
.fw_name = "rtl_bt/rtl8852cu_fw.bin",
.cfg_name = "rtl_bt/rtl8852cu_config" },
.fw_name = "rtl_bt/rtl8852cu_fw",
.cfg_name = "rtl_bt/rtl8852cu_config",
.hw_info = "rtl8852cu" },
/* 8851B */
{ IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB),
.config_needed = false,
.has_rom_version = true,
.has_msft_ext = false,
.fw_name = "rtl_bt/rtl8851bu_fw.bin",
.cfg_name = "rtl_bt/rtl8851bu_config" },
.fw_name = "rtl_bt/rtl8851bu_fw",
.cfg_name = "rtl_bt/rtl8851bu_config",
.hw_info = "rtl8851bu" },
};
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
@ -590,6 +616,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
unsigned char **_buf)
{
static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
struct rtl_epatch_header *epatch_info;
unsigned char *buf;
int i, len;
@ -705,8 +732,10 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
num_patches = le16_to_cpu(epatch_info->num_patches);
BT_DBG("fw_version=%x, num_patches=%d",
le32_to_cpu(epatch_info->fw_version), num_patches);
coredump_info->rtl_dump.fw_version = le32_to_cpu(epatch_info->fw_version);
/* After the rtl_epatch_header there is a funky patch metadata section.
* Assuming 2 patches, the layout is:
@ -903,6 +932,53 @@ out:
return ret;
}
static void btrtl_coredump(struct hci_dev *hdev)
{
static const u8 param[] = { 0x00, 0x00 };
__hci_cmd_send(hdev, RTL_VSC_OP_COREDUMP, sizeof(param), param);
}
static void btrtl_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
char buf[80];
if (coredump_info->rtl_dump.controller)
snprintf(buf, sizeof(buf), "Controller Name: %s\n",
coredump_info->rtl_dump.controller);
else
snprintf(buf, sizeof(buf), "Controller Name: Unknown\n");
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
coredump_info->rtl_dump.fw_version);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info->rtl_dump.driver_name);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Vendor: Realtek\n");
skb_put_data(skb, buf, strlen(buf));
}
static int btrtl_register_devcoredump_support(struct hci_dev *hdev)
{
int err;
err = hci_devcd_register(hdev, btrtl_coredump, btrtl_dmp_hdr, NULL);
return err;
}
void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
{
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
coredump_info->rtl_dump.driver_name = driver_name;
}
EXPORT_SYMBOL_GPL(btrtl_set_driver_name);
static bool rtl_has_chip_type(u16 lmp_subver)
{
switch (lmp_subver) {
@ -964,15 +1040,16 @@ EXPORT_SYMBOL_GPL(btrtl_free);
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix)
{
struct btrealtek_data *coredump_info = hci_get_priv(hdev);
struct btrtl_device_info *btrtl_dev;
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
struct hci_command_hdr *cmd;
char fw_name[40];
char cfg_name[40];
u16 hci_rev, lmp_subver;
u8 hci_ver, lmp_ver, chip_type = 0;
int ret;
u16 opcode;
u8 cmd[2];
u8 reg_val[2];
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
@ -1041,15 +1118,14 @@ next:
btrtl_dev->drop_fw = false;
if (btrtl_dev->drop_fw) {
opcode = hci_opcode_pack(0x3f, 0x66);
cmd[0] = opcode & 0xff;
cmd[1] = opcode >> 8;
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
skb = bt_skb_alloc(sizeof(*cmd), GFP_KERNEL);
if (!skb)
goto err_free;
skb_put_data(skb, cmd, sizeof(cmd));
cmd = skb_put(skb, HCI_COMMAND_HDR_SIZE);
cmd->opcode = cpu_to_le16(0xfc66);
cmd->plen = 0;
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
ret = hdev->send(hdev, skb);
@ -1079,8 +1155,26 @@ next:
goto err_free;
}
btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name,
&btrtl_dev->fw_data);
if (!btrtl_dev->ic_info->fw_name) {
ret = -ENOMEM;
goto err_free;
}
btrtl_dev->fw_len = -EIO;
if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
btrtl_dev->ic_info->fw_name);
btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
&btrtl_dev->fw_data);
}
if (btrtl_dev->fw_len < 0) {
snprintf(fw_name, sizeof(fw_name), "%s.bin",
btrtl_dev->ic_info->fw_name);
btrtl_dev->fw_len = rtl_load_file(hdev, fw_name,
&btrtl_dev->fw_data);
}
if (btrtl_dev->fw_len < 0) {
rtl_dev_err(hdev, "firmware file %s not found",
btrtl_dev->ic_info->fw_name);
@ -1113,6 +1207,9 @@ next:
if (btrtl_dev->ic_info->has_msft_ext)
hci_set_msft_opcode(hdev, 0xFCF0);
if (btrtl_dev->ic_info)
coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
return btrtl_dev;
err_free:
@ -1125,6 +1222,8 @@ EXPORT_SYMBOL_GPL(btrtl_initialize);
int btrtl_download_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
int err = 0;
/* Match a set of subver values that correspond to stock firmware,
* which is not compatible with standard btusb.
* If matched, upload an alternative firmware that does conform to
@ -1133,12 +1232,14 @@ int btrtl_download_firmware(struct hci_dev *hdev,
*/
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "assuming no firmware upload needed");
return 0;
err = 0;
goto done;
}
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8723A:
return btrtl_setup_rtl8723a(hdev, btrtl_dev);
err = btrtl_setup_rtl8723a(hdev, btrtl_dev);
break;
case RTL_ROM_LMP_8723B:
case RTL_ROM_LMP_8821A:
case RTL_ROM_LMP_8761A:
@ -1146,11 +1247,18 @@ int btrtl_download_firmware(struct hci_dev *hdev,
case RTL_ROM_LMP_8852A:
case RTL_ROM_LMP_8703B:
case RTL_ROM_LMP_8851B:
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
err = btrtl_setup_rtl8723b(hdev, btrtl_dev);
break;
default:
rtl_dev_info(hdev, "assuming no firmware upload needed");
return 0;
break;
}
done:
if (!err)
err = btrtl_register_devcoredump_support(hdev);
return err;
}
EXPORT_SYMBOL_GPL(btrtl_download_firmware);
@ -1180,6 +1288,10 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
if (btrtl_dev->project_id == CHIP_ID_8852C)
btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
if (btrtl_dev->project_id == CHIP_ID_8852A ||
btrtl_dev->project_id == CHIP_ID_8852C)
set_bit(HCI_QUIRK_USE_MSFT_EXT_ADDRESS_FILTER, &hdev->quirks);
hci_set_aosp_capable(hdev);
break;
default:
@ -1398,4 +1510,5 @@ MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw_v2.bin");
MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin");

View File

@ -109,8 +109,16 @@ enum {
__REALTEK_NUM_FLAGS,
};
struct rtl_dump_info {
const char *driver_name;
char *controller;
u32 fw_version;
};
struct btrealtek_data {
DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
struct rtl_dump_info rtl_dump;
};
#define btrealtek_set_flag(hdev, nr) \
@ -139,6 +147,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned int *controller_baudrate,
u32 *device_baudrate, bool *flow_control);
void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
#else
@ -182,4 +191,8 @@ static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
return -ENOENT;
}
static inline void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name)
{
}
#endif

View File

@ -175,7 +175,7 @@ static const struct usb_device_id btusb_table[] = {
MODULE_DEVICE_TABLE(usb, btusb_table);
static const struct usb_device_id blacklist_table[] = {
static const struct usb_device_id quirks_table[] = {
/* CSR BlueCore devices */
{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
@ -476,6 +476,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x8087, 0x0032), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0035), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0036), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
BTUSB_INTEL_NO_WBS_SUPPORT |
@ -625,9 +626,24 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe0f1), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe0f5), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe0f6), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x0489, 0xe102), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
{ USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH |
BTUSB_VALID_LE_STATES },
/* Additional Realtek 8723AE Bluetooth devices */
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
@ -860,10 +876,26 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
struct btintel_data *intel_data = hci_get_priv(hdev);
if (++data->cmd_timeout_cnt < 5)
return;
if (intel_data->acpi_reset_method) {
if (test_and_set_bit(INTEL_ACPI_RESET_ACTIVE, intel_data->flags)) {
bt_dev_err(hdev, "acpi: last reset failed ? Not resetting again");
return;
}
bt_dev_err(hdev, "Initiating acpi reset method");
/* If ACPI reset method fails, lets try with legacy GPIO
* toggling
*/
if (!intel_data->acpi_reset_method(hdev)) {
return;
}
}
if (!reset_gpio) {
btusb_reset(hdev);
return;
@ -887,10 +919,49 @@ static void btusb_intel_cmd_timeout(struct hci_dev *hdev)
gpiod_set_value_cansleep(reset_gpio, 0);
}
#define RTK_DEVCOREDUMP_CODE_MEMDUMP 0x01
#define RTK_DEVCOREDUMP_CODE_HW_ERR 0x02
#define RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT 0x03
#define RTK_SUB_EVENT_CODE_COREDUMP 0x34
struct rtk_dev_coredump_hdr {
u8 type;
u8 code;
u8 reserved[2];
} __packed;
static inline void btusb_rtl_alloc_devcoredump(struct hci_dev *hdev,
struct rtk_dev_coredump_hdr *hdr, u8 *buf, u32 len)
{
struct sk_buff *skb;
skb = alloc_skb(len + sizeof(*hdr), GFP_ATOMIC);
if (!skb)
return;
skb_put_data(skb, hdr, sizeof(*hdr));
if (len)
skb_put_data(skb, buf, len);
if (!hci_devcd_init(hdev, skb->len)) {
hci_devcd_append(hdev, skb);
hci_devcd_complete(hdev);
} else {
bt_dev_err(hdev, "RTL: Failed to generate devcoredump");
kfree_skb(skb);
}
}
static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct gpio_desc *reset_gpio = data->reset_gpio;
struct rtk_dev_coredump_hdr hdr = {
.type = RTK_DEVCOREDUMP_CODE_CMD_TIMEOUT,
};
btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
if (++data->cmd_timeout_cnt < 5)
return;
@ -917,6 +988,18 @@ static void btusb_rtl_cmd_timeout(struct hci_dev *hdev)
gpiod_set_value_cansleep(reset_gpio, 0);
}
static void btusb_rtl_hw_error(struct hci_dev *hdev, u8 code)
{
struct rtk_dev_coredump_hdr hdr = {
.type = RTK_DEVCOREDUMP_CODE_HW_ERR,
.code = code,
};
bt_dev_err(hdev, "RTL: hw err, trigger devcoredump (%d)", code);
btusb_rtl_alloc_devcoredump(hdev, &hdr, NULL, 0);
}
static void btusb_qca_cmd_timeout(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@ -2079,7 +2162,7 @@ static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts)
* alternate setting.
*/
spin_lock_irqsave(&data->rxlock, flags);
kfree_skb(data->sco_skb);
dev_kfree_skb_irq(data->sco_skb);
data->sco_skb = NULL;
spin_unlock_irqrestore(&data->rxlock, flags);
@ -2409,79 +2492,6 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
return btusb_recv_bulk(data, buffer, count);
}
static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
struct intel_tlv *tlv = (void *)&skb->data[5];
/* The first event is always an event type TLV */
if (tlv->type != INTEL_TLV_TYPE_ID)
goto recv_frame;
switch (tlv->val[0]) {
case INTEL_TLV_SYSTEM_EXCEPTION:
case INTEL_TLV_FATAL_EXCEPTION:
case INTEL_TLV_DEBUG_EXCEPTION:
case INTEL_TLV_TEST_EXCEPTION:
/* Generate devcoredump from exception */
if (!hci_devcd_init(hdev, skb->len)) {
hci_devcd_append(hdev, skb);
hci_devcd_complete(hdev);
} else {
bt_dev_err(hdev, "Failed to generate devcoredump");
kfree_skb(skb);
}
return 0;
default:
bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
}
recv_frame:
return hci_recv_frame(hdev, skb);
}
static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *)skb->data;
const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
hdr->plen > 0) {
const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
switch (skb->data[2]) {
case 0x02:
/* When switching to the operational firmware
* the device sends a vendor specific event
* indicating that the bootup completed.
*/
btintel_bootup(hdev, ptr, len);
break;
case 0x06:
/* When the firmware loading completes the
* device sends out a vendor specific event
* indicating the result of the firmware
* loading.
*/
btintel_secure_send_result(hdev, ptr, len);
break;
}
}
/* Handle all diagnostics events separately. May still call
* hci_recv_frame.
*/
if (len >= sizeof(diagnostics_hdr) &&
memcmp(&skb->data[2], diagnostics_hdr,
sizeof(diagnostics_hdr)) == 0) {
return btusb_intel_diagnostics(hdev, skb);
}
}
return hci_recv_frame(hdev, skb);
}
static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
{
struct urb *urb;
@ -2562,6 +2572,25 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
return ret;
}
static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
{
if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
struct rtk_dev_coredump_hdr hdr = {
.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
};
bt_dev_dbg(hdev, "RTL: received coredump vendor evt, len %u",
skb->len);
btusb_rtl_alloc_devcoredump(hdev, &hdr, skb->data, skb->len);
kfree_skb(skb);
return 0;
}
return hci_recv_frame(hdev, skb);
}
/* UHW CR mapping */
#define MTK_BT_MISC 0x70002510
#define MTK_BT_SUBSYS_RST 0x70002610
@ -2571,8 +2600,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
#define MTK_EP_RST_OPT 0x74011890
#define MTK_EP_RST_IN_OUT_OPT 0x00010001
#define MTK_BT_RST_DONE 0x00000100
#define MTK_BT_RESET_WAIT_MS 100
#define MTK_BT_RESET_NUM_TRIES 10
#define MTK_BT_RESET_REG_CONNV3 0x70028610
#define MTK_BT_READ_DEV_ID 0x70010200
static void btusb_mtk_wmt_recv(struct urb *urb)
{
@ -2943,6 +2973,88 @@ static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
return btusb_mtk_reg_read(data, reg, id);
}
static u32 btusb_mtk_reset_done(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
u32 val = 0;
btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
return val & MTK_BT_RST_DONE;
}
static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct btmediatek_data *mediatek;
u32 val;
int err;
/* It's MediaTek specific bluetooth reset mechanism via USB */
if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
bt_dev_err(hdev, "last reset failed? Not resetting again");
return -EBUSY;
}
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return err;
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
mediatek = hci_get_priv(hdev);
if (mediatek->dev_id == 0x7925) {
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
val |= (1 << 5);
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
val &= 0xFFFF00FF;
val |= (1 << 13);
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, 0x00010001);
btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
val |= (1 << 0);
btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
msleep(100);
} else {
/* It's Device EndPoint Reset Option Register */
bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
/* Reset the bluetooth chip via USB interface. */
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
/* MT7921 need to delay 20ms between toggle reset bit */
msleep(20);
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
}
err = readx_poll_timeout(btusb_mtk_reset_done, hdev, val,
val & MTK_BT_RST_DONE, 20000, 1000000);
if (err < 0)
bt_dev_err(hdev, "Reset timeout");
btusb_mtk_id_get(data, 0x70010200, &val);
if (!val)
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
usb_queue_reset_device(data->intf);
clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
return err;
}
static int btusb_mtk_setup(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@ -2953,10 +3065,11 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
struct sk_buff *skb;
const char *fwname;
int err, status;
u32 dev_id;
u32 dev_id = 0;
char fw_bin_name[64];
u32 fw_version = 0;
u8 param;
struct btmediatek_data *mediatek;
calltime = ktime_get();
@ -2966,7 +3079,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
return err;
}
if (!dev_id) {
if (!dev_id || dev_id != 0x7663) {
err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
if (err < 0) {
bt_dev_err(hdev, "Failed to get device id (%d)", err);
@ -2979,6 +3092,14 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
}
}
mediatek = hci_get_priv(hdev);
mediatek->dev_id = dev_id;
mediatek->reset_sync = btusb_mtk_reset;
err = btmtk_register_coredump(hdev, btusb_driver.name, fw_version);
if (err < 0)
bt_dev_err(hdev, "Failed to register coredump (%d)", err);
switch (dev_id) {
case 0x7663:
fwname = FIRMWARE_MT7663;
@ -2988,9 +3109,16 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
break;
case 0x7922:
case 0x7961:
snprintf(fw_bin_name, sizeof(fw_bin_name),
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, (fw_version & 0xff) + 1);
case 0x7925:
if (dev_id == 0x7925)
snprintf(fw_bin_name, sizeof(fw_bin_name),
"mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, dev_id & 0xffff, (fw_version & 0xff) + 1);
else
snprintf(fw_bin_name, sizeof(fw_bin_name),
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, (fw_version & 0xff) + 1);
err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
btusb_mtk_hci_wmt_sync);
if (err < 0) {
@ -3128,67 +3256,11 @@ static int btusb_mtk_shutdown(struct hci_dev *hdev)
return 0;
}
static void btusb_mtk_cmd_timeout(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
u32 val;
int err, retry = 0;
/* It's MediaTek specific bluetooth reset mechanism via USB */
if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
bt_dev_err(hdev, "last reset failed? Not resetting again");
return;
}
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return;
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
/* It's Device EndPoint Reset Option Register */
bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
/* Reset the bluetooth chip via USB interface. */
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
/* MT7921 need to delay 20ms between toggle reset bit */
msleep(20);
btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
/* Poll the register until reset is completed */
do {
btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
if (val & MTK_BT_RST_DONE) {
bt_dev_dbg(hdev, "Bluetooth Reset Successfully");
break;
}
bt_dev_dbg(hdev, "Polling Bluetooth Reset CR");
retry++;
msleep(MTK_BT_RESET_WAIT_MS);
} while (retry < MTK_BT_RESET_NUM_TRIES);
btusb_mtk_id_get(data, 0x70010200, &val);
if (!val)
bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
usb_queue_reset_device(data->intf);
clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
}
static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
struct sk_buff *skb_cd;
switch (handle) {
case 0xfc6f: /* Firmware dump from device */
@ -3196,6 +3268,15 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
* suspend and thus disable auto-suspend.
*/
usb_disable_autosuspend(data->udev);
/* We need to forward the diagnostic packet to userspace daemon
* for backward compatibility, so we have to clone the packet
* extraly for the in-kernel coredump support.
*/
skb_cd = skb_clone(skb, GFP_ATOMIC);
if (skb_cd)
btmtk_process_coredump(hdev, skb_cd);
fallthrough;
case 0x05ff: /* Firmware debug logging 1 */
case 0x05fe: /* Firmware debug logging 2 */
@ -4113,7 +4194,7 @@ static int btusb_probe(struct usb_interface *intf,
if (!id->driver_info) {
const struct usb_device_id *match;
match = usb_match_id(intf, blacklist_table);
match = usb_match_id(intf, quirks_table);
if (match)
id = match;
}
@ -4196,11 +4277,16 @@ static int btusb_probe(struct usb_interface *intf,
priv_size += sizeof(struct btintel_data);
/* Override the rx handlers */
data->recv_event = btusb_recv_event_intel;
data->recv_event = btintel_recv_event;
data->recv_bulk = btusb_recv_bulk_intel;
} else if (id->driver_info & BTUSB_REALTEK) {
/* Allocate extra space for Realtek device */
priv_size += sizeof(struct btrealtek_data);
data->recv_event = btusb_recv_event_realtek;
} else if (id->driver_info & BTUSB_MEDIATEK) {
/* Allocate extra space for Mediatek device */
priv_size += sizeof(struct btmediatek_data);
}
data->recv_acl = hci_recv_frame;
@ -4307,7 +4393,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->setup = btusb_mtk_setup;
hdev->shutdown = btusb_mtk_shutdown;
hdev->manufacturer = 70;
hdev->cmd_timeout = btusb_mtk_cmd_timeout;
hdev->cmd_timeout = btmtk_reset_sync;
hdev->set_bdaddr = btmtk_set_bdaddr;
set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
@ -4364,9 +4450,11 @@ static int btusb_probe(struct usb_interface *intf,
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_RTL) &&
(id->driver_info & BTUSB_REALTEK)) {
btrtl_set_driver_name(hdev, btusb_driver.name);
hdev->setup = btusb_setup_realtek;
hdev->shutdown = btrtl_shutdown_realtek;
hdev->cmd_timeout = btusb_rtl_cmd_timeout;
hdev->hw_error = btusb_rtl_hw_error;
/* Realtek devices need to set remote wakeup on auto-suspend */
set_bit(BTUSB_WAKEUP_AUTOSUSPEND, &data->flags);

View File

@ -11,7 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>

View File

@ -770,7 +770,8 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
break;
case HCIUARTGETPROTO:
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
if (test_bit(HCI_UART_PROTO_SET, &hu->flags) &&
test_bit(HCI_UART_PROTO_READY, &hu->flags))
err = hu->proto->id;
else
err = -EUNATCH;

View File

@ -734,7 +734,11 @@ static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
return err;
}
clk_prepare_enable(sysclk);
err = clk_prepare_enable(sysclk);
if (err) {
dev_err(dev, "could not enable sysclk: %d", err);
return err;
}
btdev->sysclk_speed = clk_get_rate(sysclk);
clk_disable_unprepare(sysclk);

View File

@ -25,7 +25,7 @@
#include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@ -117,9 +117,7 @@ enum qca_memdump_states {
QCA_MEMDUMP_TIMEOUT,
};
struct qca_memdump_data {
char *memdump_buf_head;
char *memdump_buf_tail;
struct qca_memdump_info {
u32 current_seq_no;
u32 received_dump;
u32 ram_dump_size;
@ -160,13 +158,15 @@ struct qca_data {
struct work_struct ws_tx_vote_off;
struct work_struct ctrl_memdump_evt;
struct delayed_work ctrl_memdump_timeout;
struct qca_memdump_data *qca_memdump;
struct qca_memdump_info *qca_memdump;
unsigned long flags;
struct completion drop_ev_comp;
wait_queue_head_t suspend_wait_q;
enum qca_memdump_states memdump_state;
struct mutex hci_memdump_lock;
u16 fw_version;
u16 controller_id;
/* For debugging purpose */
u64 ibs_sent_wacks;
u64 ibs_sent_slps;
@ -233,6 +233,7 @@ 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);
static void qca_controller_memdump(struct work_struct *work);
static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb);
static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
{
@ -606,9 +607,18 @@ static int qca_open(struct hci_uart *hu)
if (hu->serdev) {
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qca_is_wcn399x(qcadev->btsoc_type) ||
qca_is_wcn6750(qcadev->btsoc_type))
switch (qcadev->btsoc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
hu->init_speed = qcadev->init_speed;
break;
default:
break;
}
if (qcadev->oper_speed)
hu->oper_speed = qcadev->oper_speed;
@ -980,6 +990,28 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
char buf[80];
snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n",
qca->controller_id);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n",
qca->fw_version);
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Vendor:Qualcomm\n");
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Driver: %s\n",
hu->serdev->dev.driver->name);
skb_put_data(skb, buf, strlen(buf));
}
static void qca_controller_memdump(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
@ -987,13 +1019,11 @@ static void qca_controller_memdump(struct work_struct *work)
struct hci_uart *hu = qca->hu;
struct sk_buff *skb;
struct qca_memdump_event_hdr *cmd_hdr;
struct qca_memdump_data *qca_memdump = qca->qca_memdump;
struct qca_memdump_info *qca_memdump = qca->qca_memdump;
struct qca_dump_size *dump;
char *memdump_buf;
char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
u32 dump_size;
u32 rx_size;
int ret = 0;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
@ -1009,7 +1039,7 @@ static void qca_controller_memdump(struct work_struct *work)
}
if (!qca_memdump) {
qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
qca_memdump = kzalloc(sizeof(struct qca_memdump_info),
GFP_ATOMIC);
if (!qca_memdump) {
mutex_unlock(&qca->hci_memdump_lock);
@ -1035,44 +1065,49 @@ static void qca_controller_memdump(struct work_struct *work)
set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data;
dump_size = __le32_to_cpu(dump->dump_size);
if (!(dump_size)) {
qca_memdump->ram_dump_size = __le32_to_cpu(dump->dump_size);
if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
kfree_skb(skb);
mutex_unlock(&qca->hci_memdump_lock);
return;
}
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
qca_memdump->current_seq_no = 0;
qca_memdump->received_dump = 0;
ret = hci_devcd_init(hu->hdev, qca_memdump->ram_dump_size);
bt_dev_info(hu->hdev, "hci_devcd_init Return:%d",
ret);
if (ret < 0) {
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
qca->memdump_state = QCA_MEMDUMP_COLLECTED;
cancel_delayed_work(&qca->ctrl_memdump_timeout);
clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
mutex_unlock(&qca->hci_memdump_lock);
return;
}
bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
dump_size);
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
);
qca_memdump->ram_dump_size);
skb_pull(skb, sizeof(dump_size));
memdump_buf = vmalloc(dump_size);
qca_memdump->ram_dump_size = dump_size;
qca_memdump->memdump_buf_head = memdump_buf;
qca_memdump->memdump_buf_tail = memdump_buf;
}
memdump_buf = qca_memdump->memdump_buf_tail;
/* If sequence no 0 is missed then there is no point in
* accepting the other sequences.
*/
if (!memdump_buf) {
if (!test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
kfree(qca_memdump);
kfree_skb(skb);
qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
/* There could be chance of missing some packets from
* the controller. In such cases let us store the dummy
* packets in the buffer.
@ -1082,8 +1117,8 @@ static void qca_controller_memdump(struct work_struct *work)
* bits, so skip this checking for missing packet.
*/
while ((seq_no > qca_memdump->current_seq_no + 1) &&
(soc_type != QCA_QCA6390) &&
seq_no != QCA_LAST_SEQUENCE_NUM) {
(soc_type != QCA_QCA6390) &&
seq_no != QCA_LAST_SEQUENCE_NUM) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
rx_size = qca_memdump->received_dump;
@ -1094,43 +1129,38 @@ static void qca_controller_memdump(struct work_struct *work)
qca_memdump->received_dump);
break;
}
memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
hci_devcd_append_pattern(hu->hdev, 0x00,
QCA_DUMP_PACKET_SIZE);
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
qca_memdump->current_seq_no++;
}
rx_size = qca_memdump->received_dump + skb->len;
rx_size = qca_memdump->received_dump + skb->len;
if (rx_size <= qca_memdump->ram_dump_size) {
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
(seq_no != qca_memdump->current_seq_no))
(seq_no != qca_memdump->current_seq_no)) {
bt_dev_err(hu->hdev,
"QCA memdump unexpected packet %d",
seq_no);
}
bt_dev_dbg(hu->hdev,
"QCA memdump packet %d with length %d",
seq_no, skb->len);
memcpy(memdump_buf, (unsigned char *)skb->data,
skb->len);
memdump_buf = memdump_buf + skb->len;
qca_memdump->memdump_buf_tail = memdump_buf;
qca_memdump->current_seq_no = seq_no + 1;
qca_memdump->received_dump += skb->len;
hci_devcd_append(hu->hdev, skb);
qca_memdump->current_seq_no += 1;
qca_memdump->received_dump = rx_size;
} else {
bt_dev_err(hu->hdev,
"QCA memdump received %d, no space for packet %d",
qca_memdump->received_dump, seq_no);
"QCA memdump received no space for packet %d",
qca_memdump->current_seq_no);
}
qca->qca_memdump = qca_memdump;
kfree_skb(skb);
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
bt_dev_info(hu->hdev,
"QCA memdump Done, received %d, total %d",
qca_memdump->received_dump,
qca_memdump->ram_dump_size);
memdump_buf = qca_memdump->memdump_buf_head;
dev_coredumpv(&hu->serdev->dev, memdump_buf,
qca_memdump->received_dump, GFP_KERNEL);
"QCA memdump Done, received %d, total %d",
qca_memdump->received_dump,
qca_memdump->ram_dump_size);
hci_devcd_complete(hu->hdev);
cancel_delayed_work(&qca->ctrl_memdump_timeout);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
@ -1320,12 +1350,20 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
/* Give the controller time to process the request */
if (qca_is_wcn399x(qca_soc_type(hu)) ||
qca_is_wcn6750(qca_soc_type(hu)) ||
qca_is_wcn6855(qca_soc_type(hu)))
switch (qca_soc_type(hu)) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
usleep_range(1000, 10000);
else
break;
default:
msleep(300);
}
return 0;
}
@ -1398,13 +1436,20 @@ static unsigned int qca_get_speed(struct hci_uart *hu,
static int qca_check_speeds(struct hci_uart *hu)
{
if (qca_is_wcn399x(qca_soc_type(hu)) ||
qca_is_wcn6750(qca_soc_type(hu)) ||
qca_is_wcn6855(qca_soc_type(hu))) {
switch (qca_soc_type(hu)) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
if (!qca_get_speed(hu, QCA_INIT_SPEED) &&
!qca_get_speed(hu, QCA_OPER_SPEED))
return -EINVAL;
} else {
break;
default:
if (!qca_get_speed(hu, QCA_INIT_SPEED) ||
!qca_get_speed(hu, QCA_OPER_SPEED))
return -EINVAL;
@ -1433,14 +1478,29 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
/* Disable flow control for wcn3990 to deassert RTS while
* changing the baudrate of chip and host.
*/
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type) ||
qca_is_wcn6855(soc_type))
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
hci_uart_set_flow_control(hu, true);
break;
if (soc_type == QCA_WCN3990) {
default:
break;
}
switch (soc_type) {
case QCA_WCN3990:
reinit_completion(&qca->drop_ev_comp);
set_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
break;
default:
break;
}
qca_baudrate = qca_get_baudrate_value(speed);
@ -1452,12 +1512,23 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
host_set_baudrate(hu, speed);
error:
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type) ||
qca_is_wcn6855(soc_type))
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
hci_uart_set_flow_control(hu, false);
break;
if (soc_type == QCA_WCN3990) {
default:
break;
}
switch (soc_type) {
case QCA_WCN3990:
/* Wait for the controller to send the vendor event
* for the baudrate change command.
*/
@ -1469,6 +1540,10 @@ error:
}
clear_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
break;
default:
break;
}
}
@ -1541,8 +1616,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
hci_devcd_abort(hu->hdev);
if (qca->qca_memdump) {
vfree(qca->qca_memdump->memdump_buf_head);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
}
@ -1630,12 +1705,20 @@ static int qca_regulator_init(struct hci_uart *hu)
}
}
if (qca_is_wcn399x(soc_type)) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
/* Forcefully enable wcn399x to enter in to boot mode. */
host_set_baudrate(hu, 2400);
ret = qca_send_power_pulse(hu, false);
if (ret)
return ret;
break;
default:
break;
}
/* For wcn6750 need to enable gpio bt_en */
@ -1652,10 +1735,18 @@ static int qca_regulator_init(struct hci_uart *hu)
qca_set_speed(hu, QCA_INIT_SPEED);
if (qca_is_wcn399x(soc_type)) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
ret = qca_send_power_pulse(hu, true);
if (ret)
return ret;
break;
default:
break;
}
/* Now the device is in ready state to communicate with host.
@ -1689,11 +1780,18 @@ static int qca_power_on(struct hci_dev *hdev)
if (!hu->serdev)
return 0;
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type) ||
qca_is_wcn6855(soc_type)) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
ret = qca_regulator_init(hu);
} else {
break;
default:
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qcadev->bt_en) {
gpiod_set_value_cansleep(qcadev->bt_en, 1);
@ -1706,6 +1804,17 @@ static int qca_power_on(struct hci_dev *hdev)
return ret;
}
static void hci_coredump_qca(struct hci_dev *hdev)
{
static const u8 param[] = { 0x26 };
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc0c, 1, param, HCI_CMD_TIMEOUT);
if (IS_ERR(skb))
bt_dev_err(hdev, "%s: trigger crash failed (%ld)", __func__, PTR_ERR(skb));
kfree_skb(skb);
}
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
@ -1716,6 +1825,7 @@ static int qca_setup(struct hci_uart *hu)
const char *firmware_name = qca_get_firmware_name(hu);
int ret;
struct qca_btsoc_version ver;
const char *soc_name;
ret = qca_check_speeds(hu);
if (ret)
@ -1730,10 +1840,30 @@ static int qca_setup(struct hci_uart *hu)
*/
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
bt_dev_info(hdev, "setting up %s",
qca_is_wcn399x(soc_type) ? "wcn399x" :
(soc_type == QCA_WCN6750) ? "wcn6750" :
(soc_type == QCA_WCN6855) ? "wcn6855" : "ROME/QCA6390");
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
soc_name = "wcn399x";
break;
case QCA_WCN6750:
soc_name = "wcn6750";
break;
case QCA_WCN6855:
soc_name = "wcn6855";
break;
case QCA_WCN7850:
soc_name = "wcn7850";
break;
default:
soc_name = "ROME/QCA6390";
}
bt_dev_info(hdev, "setting up %s", soc_name);
qca->memdump_state = QCA_MEMDUMP_IDLE;
@ -1744,16 +1874,23 @@ retry:
clear_bit(QCA_SSR_TRIGGERED, &qca->flags);
if (qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type) ||
qca_is_wcn6855(soc_type)) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
hci_set_aosp_capable(hdev);
ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret)
goto out;
} else {
break;
default:
qca_set_speed(hu, QCA_INIT_SPEED);
}
@ -1767,9 +1904,17 @@ retry:
qca_baudrate = qca_get_baudrate_value(speed);
}
if (!(qca_is_wcn399x(soc_type) ||
qca_is_wcn6750(soc_type) ||
qca_is_wcn6855(soc_type))) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
break;
default:
/* Get QCA version information */
ret = qca_read_soc_version(hdev, &ver, soc_type);
if (ret)
@ -1820,6 +1965,9 @@ out:
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
else
hu->hdev->set_bdaddr = qca_set_bdaddr;
qca->fw_version = le16_to_cpu(ver.patch_ver);
qca->controller_id = le16_to_cpu(ver.rom_ver);
hci_devcd_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
return ret;
}
@ -1839,6 +1987,17 @@ static const struct hci_uart_proto qca_proto = {
.dequeue = qca_dequeue,
};
static const struct qca_device_data qca_soc_data_wcn3988 __maybe_unused = {
.soc_type = QCA_WCN3988,
.vregs = (struct qca_vreg []) {
{ "vddio", 15000 },
{ "vddxo", 80000 },
{ "vddrf", 300000 },
{ "vddch0", 450000 },
},
.num_vregs = 4,
};
static const struct qca_device_data qca_soc_data_wcn3990 __maybe_unused = {
.soc_type = QCA_WCN3990,
.vregs = (struct qca_vreg []) {
@ -1909,6 +2068,20 @@ static const struct qca_device_data qca_soc_data_wcn6855 __maybe_unused = {
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
};
static const struct qca_device_data qca_soc_data_wcn7850 __maybe_unused = {
.soc_type = QCA_WCN7850,
.vregs = (struct qca_vreg []) {
{ "vddio", 5000 },
{ "vddaon", 26000 },
{ "vdddig", 126000 },
{ "vddrfa0p8", 102000 },
{ "vddrfa1p2", 257000 },
{ "vddrfa1p9", 302000 },
},
.num_vregs = 6,
.capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES,
};
static void qca_power_shutdown(struct hci_uart *hu)
{
struct qca_serdev *qcadev;
@ -1934,11 +2107,18 @@ static void qca_power_shutdown(struct hci_uart *hu)
qcadev = serdev_device_get_drvdata(hu->serdev);
if (qca_is_wcn399x(soc_type)) {
switch (soc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
qca_regulator_disable(qcadev);
} else if (soc_type == QCA_WCN6750 || soc_type == QCA_WCN6855) {
break;
case QCA_WCN6750:
case QCA_WCN6855:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
msleep(100);
qca_regulator_disable(qcadev);
@ -1946,7 +2126,9 @@ static void qca_power_shutdown(struct hci_uart *hu)
sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
}
} else if (qcadev->bt_en) {
break;
default:
gpiod_set_value_cansleep(qcadev->bt_en, 0);
}
@ -2071,11 +2253,19 @@ static int qca_serdev_probe(struct serdev_device *serdev)
if (!qcadev->oper_speed)
BT_DBG("UART will pick default operating speed");
if (data &&
(qca_is_wcn399x(data->soc_type) ||
qca_is_wcn6750(data->soc_type) ||
qca_is_wcn6855(data->soc_type))) {
if (data)
qcadev->btsoc_type = data->soc_type;
else
qcadev->btsoc_type = QCA_ROME;
switch (qcadev->btsoc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
qcadev->bt_power = devm_kzalloc(&serdev->dev,
sizeof(struct qca_power),
GFP_KERNEL);
@ -2105,7 +2295,8 @@ static int qca_serdev_probe(struct serdev_device *serdev)
GPIOD_IN);
if (IS_ERR_OR_NULL(qcadev->sw_ctrl) &&
(data->soc_type == QCA_WCN6750 ||
data->soc_type == QCA_WCN6855))
data->soc_type == QCA_WCN6855 ||
data->soc_type == QCA_WCN7850))
dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
@ -2119,12 +2310,9 @@ static int qca_serdev_probe(struct serdev_device *serdev)
BT_ERR("wcn3990 serdev registration failed");
return err;
}
} else {
if (data)
qcadev->btsoc_type = data->soc_type;
else
qcadev->btsoc_type = QCA_ROME;
break;
default:
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR_OR_NULL(qcadev->bt_en)) {
@ -2180,13 +2368,24 @@ static void qca_serdev_remove(struct serdev_device *serdev)
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
struct qca_power *power = qcadev->bt_power;
if ((qca_is_wcn399x(qcadev->btsoc_type) ||
qca_is_wcn6750(qcadev->btsoc_type) ||
qca_is_wcn6855(qcadev->btsoc_type)) &&
power->vregs_on)
qca_power_shutdown(&qcadev->serdev_hu);
else if (qcadev->susclk)
clk_disable_unprepare(qcadev->susclk);
switch (qcadev->btsoc_type) {
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
if (power->vregs_on) {
qca_power_shutdown(&qcadev->serdev_hu);
break;
}
fallthrough;
default:
if (qcadev->susclk)
clk_disable_unprepare(qcadev->susclk);
}
hci_uart_unregister_device(&qcadev->serdev_hu);
}
@ -2363,11 +2562,13 @@ static const struct of_device_id qca_bluetooth_of_match[] = {
{ .compatible = "qcom,qca6174-bt" },
{ .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
{ .compatible = "qcom,qca9377-bt" },
{ .compatible = "qcom,wcn3988-bt", .data = &qca_soc_data_wcn3988},
{ .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},
{ .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750},
{ .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855},
{ .compatible = "qcom,wcn7850-bt", .data = &qca_soc_data_wcn7850},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
@ -2384,6 +2585,18 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
#endif
#ifdef CONFIG_DEV_COREDUMP
static void hciqca_coredump(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
struct hci_uart *hu = &qcadev->serdev_hu;
struct hci_dev *hdev = hu->hdev;
if (hdev->dump.coredump)
hdev->dump.coredump(hdev);
}
#endif
static struct serdev_device_driver qca_serdev_driver = {
.probe = qca_serdev_probe,
@ -2394,6 +2607,9 @@ static struct serdev_device_driver qca_serdev_driver = {
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
.shutdown = qca_serdev_shutdown,
.pm = &qca_pm_ops,
#ifdef CONFIG_DEV_COREDUMP
.coredump = hciqca_coredump,
#endif
},
};

View File

@ -48,8 +48,47 @@ static DEFINE_PER_CPU(struct local_event, local_event) = {
.lock = INIT_LOCAL_LOCK(lock),
};
static int cn_filter(struct sock *dsk, struct sk_buff *skb, void *data)
{
__u32 what, exit_code, *ptr;
enum proc_cn_mcast_op mc_op;
uintptr_t val;
if (!dsk || !data)
return 0;
ptr = (__u32 *)data;
what = *ptr++;
exit_code = *ptr;
val = ((struct proc_input *)(dsk->sk_user_data))->event_type;
mc_op = ((struct proc_input *)(dsk->sk_user_data))->mcast_op;
if (mc_op == PROC_CN_MCAST_IGNORE)
return 1;
if ((__u32)val == PROC_EVENT_ALL)
return 0;
/*
* Drop packet if we have to report only non-zero exit status
* (PROC_EVENT_NONZERO_EXIT) and exit status is 0
*/
if (((__u32)val & PROC_EVENT_NONZERO_EXIT) &&
(what == PROC_EVENT_EXIT)) {
if (exit_code)
return 0;
}
if ((__u32)val & what)
return 0;
return 1;
}
static inline void send_msg(struct cn_msg *msg)
{
__u32 filter_data[2];
local_lock(&local_event.lock);
msg->seq = __this_cpu_inc_return(local_event.count) - 1;
@ -61,7 +100,16 @@ static inline void send_msg(struct cn_msg *msg)
*
* If cn_netlink_send() fails, the data is not sent.
*/
cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_NOWAIT);
filter_data[0] = ((struct proc_event *)msg->data)->what;
if (filter_data[0] == PROC_EVENT_EXIT) {
filter_data[1] =
((struct proc_event *)msg->data)->event_data.exit.exit_code;
} else {
filter_data[1] = 0;
}
cn_netlink_send_mult(msg, msg->len, 0, CN_IDX_PROC, GFP_NOWAIT,
cn_filter, (void *)filter_data);
local_unlock(&local_event.lock);
}
@ -341,16 +389,17 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
/**
* cn_proc_mcast_ctl
* @data: message sent from userspace via the connector
* @msg: message sent from userspace via the connector
* @nsp: NETLINK_CB of the client's socket buffer
*/
static void cn_proc_mcast_ctl(struct cn_msg *msg,
struct netlink_skb_parms *nsp)
{
enum proc_cn_mcast_op *mc_op = NULL;
int err = 0;
if (msg->len != sizeof(*mc_op))
return;
enum proc_cn_mcast_op mc_op = 0, prev_mc_op = 0;
struct proc_input *pinput = NULL;
enum proc_cn_event ev_type = 0;
int err = 0, initial = 0;
struct sock *sk = NULL;
/*
* Events are reported with respect to the initial pid
@ -361,19 +410,51 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
!task_is_in_init_pid_ns(current))
return;
/* Can only change if privileged. */
if (!__netlink_ns_capable(nsp, &init_user_ns, CAP_NET_ADMIN)) {
err = EPERM;
goto out;
if (msg->len == sizeof(*pinput)) {
pinput = (struct proc_input *)msg->data;
mc_op = pinput->mcast_op;
ev_type = pinput->event_type;
} else if (msg->len == sizeof(mc_op)) {
mc_op = *((enum proc_cn_mcast_op *)msg->data);
ev_type = PROC_EVENT_ALL;
} else {
return;
}
mc_op = (enum proc_cn_mcast_op *)msg->data;
switch (*mc_op) {
ev_type = valid_event((enum proc_cn_event)ev_type);
if (ev_type == PROC_EVENT_NONE)
ev_type = PROC_EVENT_ALL;
if (nsp->sk) {
sk = nsp->sk;
if (sk->sk_user_data == NULL) {
sk->sk_user_data = kzalloc(sizeof(struct proc_input),
GFP_KERNEL);
if (sk->sk_user_data == NULL) {
err = ENOMEM;
goto out;
}
initial = 1;
} else {
prev_mc_op =
((struct proc_input *)(sk->sk_user_data))->mcast_op;
}
((struct proc_input *)(sk->sk_user_data))->event_type =
ev_type;
((struct proc_input *)(sk->sk_user_data))->mcast_op = mc_op;
}
switch (mc_op) {
case PROC_CN_MCAST_LISTEN:
atomic_inc(&proc_event_num_listeners);
if (initial || (prev_mc_op != PROC_CN_MCAST_LISTEN))
atomic_inc(&proc_event_num_listeners);
break;
case PROC_CN_MCAST_IGNORE:
atomic_dec(&proc_event_num_listeners);
if (!initial && (prev_mc_op != PROC_CN_MCAST_IGNORE))
atomic_dec(&proc_event_num_listeners);
((struct proc_input *)(sk->sk_user_data))->event_type =
PROC_EVENT_NONE;
break;
default:
err = EINVAL;

View File

@ -59,7 +59,9 @@ static int cn_already_initialized;
* both, or if both are zero then the group is looked up and sent there.
*/
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
gfp_t gfp_mask)
gfp_t gfp_mask,
int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
void *filter_data)
{
struct cn_callback_entry *__cbq;
unsigned int size;
@ -110,8 +112,9 @@ int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
NETLINK_CB(skb).dst_group = group;
if (group)
return netlink_broadcast(dev->nls, skb, portid, group,
gfp_mask);
return netlink_broadcast_filtered(dev->nls, skb, portid, group,
gfp_mask, filter,
(void *)filter_data);
return netlink_unicast(dev->nls, skb, portid,
!gfpflags_allow_blocking(gfp_mask));
}
@ -121,7 +124,8 @@ EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
gfp_t gfp_mask)
{
return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask,
NULL, NULL);
}
EXPORT_SYMBOL_GPL(cn_netlink_send);
@ -162,6 +166,31 @@ static int cn_call_callback(struct sk_buff *skb)
return err;
}
/*
* Allow non-root access for NETLINK_CONNECTOR family having CN_IDX_PROC
* multicast group.
*/
static int cn_bind(struct net *net, int group)
{
unsigned long groups = (unsigned long) group;
if (ns_capable(net->user_ns, CAP_NET_ADMIN))
return 0;
if (test_bit(CN_IDX_PROC - 1, &groups))
return 0;
return -EPERM;
}
static void cn_release(struct sock *sk, unsigned long *groups)
{
if (groups && test_bit(CN_IDX_PROC - 1, groups)) {
kfree(sk->sk_user_data);
sk->sk_user_data = NULL;
}
}
/*
* Main netlink receiving function.
*
@ -249,6 +278,9 @@ static int cn_init(void)
struct netlink_kernel_cfg cfg = {
.groups = CN_NETLINK_USERS + 0xf,
.input = cn_rx_skb,
.flags = NL_CFG_F_NONROOT_RECV,
.bind = cn_bind,
.release = cn_release,
};
dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);

View File

@ -58,7 +58,7 @@ entrypoints.lskel.h: $(OUTPUT)/entrypoints.bpf.o | $(BPFTOOL)
$(OUTPUT)/entrypoints.bpf.o: entrypoints.bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \
$(Q)$(CLANG) -g -O2 --target=bpf $(INCLUDES) \
-c $(filter %.c,$^) -o $@ && \
$(LLVM_STRIP) -g $@

View File

@ -400,6 +400,9 @@ static void del_gid(struct ib_device *ib_dev, u32 port,
table->data_vec[ix] = NULL;
write_unlock_irq(&table->rwlock);
if (rdma_cap_roce_gid_table(ib_dev, port))
ib_dev->ops.del_gid(&entry->attr, &entry->context);
ndev_storage = entry->ndev_storage;
if (ndev_storage) {
entry->ndev_storage = NULL;
@ -407,9 +410,6 @@ static void del_gid(struct ib_device *ib_dev, u32 port,
call_rcu(&ndev_storage->rcu_head, put_gid_ndev);
}
if (rdma_cap_roce_gid_table(ib_dev, port))
ib_dev->ops.del_gid(&entry->attr, &entry->context);
put_gid_entry_locked(entry);
}

View File

@ -82,6 +82,8 @@ static const char mlx4_ib_version[] =
static void do_slave_init(struct mlx4_ib_dev *ibdev, int slave, int do_init);
static enum rdma_link_layer mlx4_ib_port_link_layer(struct ib_device *device,
u32 port_num);
static int mlx4_ib_event(struct notifier_block *this, unsigned long event,
void *param);
static struct workqueue_struct *wq;
@ -125,12 +127,14 @@ static struct net_device *mlx4_ib_get_netdev(struct ib_device *device,
u32 port_num)
{
struct mlx4_ib_dev *ibdev = to_mdev(device);
struct net_device *dev;
struct net_device *dev, *ret = NULL;
rcu_read_lock();
dev = mlx4_get_protocol_dev(ibdev->dev, MLX4_PROT_ETH, port_num);
for_each_netdev_rcu(&init_net, dev) {
if (dev->dev.parent != ibdev->ib_dev.dev.parent ||
dev->dev_port + 1 != port_num)
continue;
if (dev) {
if (mlx4_is_bonded(ibdev->dev)) {
struct net_device *upper = NULL;
@ -143,11 +147,14 @@ static struct net_device *mlx4_ib_get_netdev(struct ib_device *device,
dev = active;
}
}
dev_hold(dev);
ret = dev;
break;
}
dev_hold(dev);
rcu_read_unlock();
return dev;
return ret;
}
static int mlx4_ib_update_gids_v1(struct gid_entry *gids,
@ -2319,61 +2326,53 @@ unlock:
mutex_unlock(&ibdev->qp1_proxy_lock[port - 1]);
}
static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev,
struct net_device *dev,
unsigned long event)
static void mlx4_ib_scan_netdev(struct mlx4_ib_dev *ibdev,
struct net_device *dev,
unsigned long event)
{
struct mlx4_ib_iboe *iboe;
int update_qps_port = -1;
int port;
struct mlx4_ib_iboe *iboe = &ibdev->iboe;
ASSERT_RTNL();
iboe = &ibdev->iboe;
if (dev->dev.parent != ibdev->ib_dev.dev.parent)
return;
spin_lock_bh(&iboe->lock);
mlx4_foreach_ib_transport_port(port, ibdev->dev) {
iboe->netdevs[port - 1] =
mlx4_get_protocol_dev(ibdev->dev, MLX4_PROT_ETH, port);
iboe->netdevs[dev->dev_port] = event != NETDEV_UNREGISTER ? dev : NULL;
if (dev == iboe->netdevs[port - 1] &&
(event == NETDEV_CHANGEADDR || event == NETDEV_REGISTER ||
event == NETDEV_UP || event == NETDEV_CHANGE))
update_qps_port = port;
if (event == NETDEV_UP || event == NETDEV_DOWN) {
enum ib_port_state port_state;
struct ib_event ibev = { };
if (dev == iboe->netdevs[port - 1] &&
(event == NETDEV_UP || event == NETDEV_DOWN)) {
enum ib_port_state port_state;
struct ib_event ibev = { };
if (ib_get_cached_port_state(&ibdev->ib_dev, dev->dev_port + 1,
&port_state))
goto iboe_out;
if (ib_get_cached_port_state(&ibdev->ib_dev, port,
&port_state))
continue;
if (event == NETDEV_UP &&
(port_state != IB_PORT_ACTIVE ||
iboe->last_port_state[port - 1] != IB_PORT_DOWN))
continue;
if (event == NETDEV_DOWN &&
(port_state != IB_PORT_DOWN ||
iboe->last_port_state[port - 1] != IB_PORT_ACTIVE))
continue;
iboe->last_port_state[port - 1] = port_state;
ibev.device = &ibdev->ib_dev;
ibev.element.port_num = port;
ibev.event = event == NETDEV_UP ? IB_EVENT_PORT_ACTIVE :
IB_EVENT_PORT_ERR;
ib_dispatch_event(&ibev);
}
if (event == NETDEV_UP &&
(port_state != IB_PORT_ACTIVE ||
iboe->last_port_state[dev->dev_port] != IB_PORT_DOWN))
goto iboe_out;
if (event == NETDEV_DOWN &&
(port_state != IB_PORT_DOWN ||
iboe->last_port_state[dev->dev_port] != IB_PORT_ACTIVE))
goto iboe_out;
iboe->last_port_state[dev->dev_port] = port_state;
ibev.device = &ibdev->ib_dev;
ibev.element.port_num = dev->dev_port + 1;
ibev.event = event == NETDEV_UP ? IB_EVENT_PORT_ACTIVE :
IB_EVENT_PORT_ERR;
ib_dispatch_event(&ibev);
}
iboe_out:
spin_unlock_bh(&iboe->lock);
if (update_qps_port > 0)
mlx4_ib_update_qps(ibdev, dev, update_qps_port);
if (event == NETDEV_CHANGEADDR || event == NETDEV_REGISTER ||
event == NETDEV_UP || event == NETDEV_CHANGE)
mlx4_ib_update_qps(ibdev, dev, dev->dev_port + 1);
}
static int mlx4_ib_netdev_event(struct notifier_block *this,
@ -2386,7 +2385,7 @@ static int mlx4_ib_netdev_event(struct notifier_block *this,
return NOTIFY_DONE;
ibdev = container_of(this, struct mlx4_ib_dev, iboe.nb);
mlx4_ib_scan_netdevs(ibdev, dev, event);
mlx4_ib_scan_netdev(ibdev, dev, event);
return NOTIFY_DONE;
}
@ -2610,8 +2609,11 @@ static const struct ib_device_ops mlx4_ib_dev_fs_ops = {
.destroy_flow = mlx4_ib_destroy_flow,
};
static void *mlx4_ib_add(struct mlx4_dev *dev)
static int mlx4_ib_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx4_adev *madev = container_of(adev, struct mlx4_adev, adev);
struct mlx4_dev *dev = madev->mdev;
struct mlx4_ib_dev *ibdev;
int num_ports = 0;
int i, j;
@ -2631,27 +2633,31 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
/* No point in registering a device with no ports... */
if (num_ports == 0)
return NULL;
return -ENODEV;
ibdev = ib_alloc_device(mlx4_ib_dev, ib_dev);
if (!ibdev) {
dev_err(&dev->persist->pdev->dev,
"Device struct alloc failed\n");
return NULL;
return -ENOMEM;
}
iboe = &ibdev->iboe;
if (mlx4_pd_alloc(dev, &ibdev->priv_pdn))
err = mlx4_pd_alloc(dev, &ibdev->priv_pdn);
if (err)
goto err_dealloc;
if (mlx4_uar_alloc(dev, &ibdev->priv_uar))
err = mlx4_uar_alloc(dev, &ibdev->priv_uar);
if (err)
goto err_pd;
ibdev->uar_map = ioremap((phys_addr_t) ibdev->priv_uar.pfn << PAGE_SHIFT,
PAGE_SIZE);
if (!ibdev->uar_map)
if (!ibdev->uar_map) {
err = -ENOMEM;
goto err_uar;
}
MLX4_INIT_DOORBELL_LOCK(&ibdev->uar_lock);
ibdev->dev = dev;
@ -2695,7 +2701,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
spin_lock_init(&iboe->lock);
if (init_node_data(ibdev))
err = init_node_data(ibdev);
if (err)
goto err_map;
mlx4_init_sl2vl_tbl(ibdev);
@ -2727,6 +2734,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
new_counter_index = kmalloc(sizeof(*new_counter_index),
GFP_KERNEL);
if (!new_counter_index) {
err = -ENOMEM;
if (allocated)
mlx4_counter_free(ibdev->dev, counter_index);
goto err_counter;
@ -2744,8 +2752,10 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
new_counter_index =
kmalloc(sizeof(struct counter_index),
GFP_KERNEL);
if (!new_counter_index)
if (!new_counter_index) {
err = -ENOMEM;
goto err_counter;
}
new_counter_index->index = counter_index;
new_counter_index->allocated = 0;
list_add_tail(&new_counter_index->list,
@ -2774,8 +2784,10 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_uc_qpns_bitmap = bitmap_alloc(ibdev->steer_qpn_count,
GFP_KERNEL);
if (!ibdev->ib_uc_qpns_bitmap)
if (!ibdev->ib_uc_qpns_bitmap) {
err = -ENOMEM;
goto err_steer_qp_release;
}
if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) {
bitmap_zero(ibdev->ib_uc_qpns_bitmap,
@ -2795,17 +2807,21 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
for (j = 1; j <= ibdev->dev->caps.num_ports; j++)
atomic64_set(&iboe->mac[j - 1], ibdev->dev->caps.def_mac[j]);
if (mlx4_ib_alloc_diag_counters(ibdev))
err = mlx4_ib_alloc_diag_counters(ibdev);
if (err)
goto err_steer_free_bitmap;
if (ib_register_device(&ibdev->ib_dev, "mlx4_%d",
&dev->persist->pdev->dev))
err = ib_register_device(&ibdev->ib_dev, "mlx4_%d",
&dev->persist->pdev->dev);
if (err)
goto err_diag_counters;
if (mlx4_ib_mad_init(ibdev))
err = mlx4_ib_mad_init(ibdev);
if (err)
goto err_reg;
if (mlx4_ib_init_sriov(ibdev))
err = mlx4_ib_init_sriov(ibdev);
if (err)
goto err_mad;
if (!iboe->nb.notifier_call) {
@ -2839,7 +2855,14 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
do_slave_init(ibdev, j, 1);
}
}
return ibdev;
/* register mlx4 core notifier */
ibdev->mlx_nb.notifier_call = mlx4_ib_event;
err = mlx4_register_event_notifier(dev, &ibdev->mlx_nb);
WARN(err, "failed to register mlx4 event notifier (%d)", err);
auxiliary_set_drvdata(adev, ibdev);
return 0;
err_notif:
if (ibdev->iboe.nb.notifier_call) {
@ -2883,7 +2906,7 @@ err_pd:
err_dealloc:
ib_dealloc_device(&ibdev->ib_dev);
return NULL;
return err;
}
int mlx4_ib_steer_qp_alloc(struct mlx4_ib_dev *dev, int count, int *qpn)
@ -2950,12 +2973,16 @@ int mlx4_ib_steer_qp_reg(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp,
return err;
}
static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr)
static void mlx4_ib_remove(struct auxiliary_device *adev)
{
struct mlx4_ib_dev *ibdev = ibdev_ptr;
struct mlx4_adev *madev = container_of(adev, struct mlx4_adev, adev);
struct mlx4_dev *dev = madev->mdev;
struct mlx4_ib_dev *ibdev = auxiliary_get_drvdata(adev);
int p;
int i;
mlx4_unregister_event_notifier(dev, &ibdev->mlx_nb);
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
devlink_port_type_clear(mlx4_get_devlink_port(dev, i));
ibdev->ib_active = false;
@ -3176,11 +3203,13 @@ void mlx4_sched_ib_sl2vl_update_work(struct mlx4_ib_dev *ibdev,
}
}
static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
enum mlx4_dev_event event, unsigned long param)
static int mlx4_ib_event(struct notifier_block *this, unsigned long event,
void *param)
{
struct mlx4_ib_dev *ibdev =
container_of(this, struct mlx4_ib_dev, mlx_nb);
struct mlx4_dev *dev = ibdev->dev;
struct ib_event ibev;
struct mlx4_ib_dev *ibdev = to_mdev((struct ib_device *) ibdev_ptr);
struct mlx4_eqe *eqe = NULL;
struct ib_event_work *ew;
int p = 0;
@ -3190,22 +3219,28 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
(event == MLX4_DEV_EVENT_PORT_DOWN))) {
ew = kmalloc(sizeof(*ew), GFP_ATOMIC);
if (!ew)
return;
return NOTIFY_DONE;
INIT_WORK(&ew->work, handle_bonded_port_state_event);
ew->ib_dev = ibdev;
queue_work(wq, &ew->work);
return;
return NOTIFY_DONE;
}
if (event == MLX4_DEV_EVENT_PORT_MGMT_CHANGE)
switch (event) {
case MLX4_DEV_EVENT_CATASTROPHIC_ERROR:
break;
case MLX4_DEV_EVENT_PORT_MGMT_CHANGE:
eqe = (struct mlx4_eqe *)param;
else
p = (int) param;
break;
default:
p = *(int *)param;
break;
}
switch (event) {
case MLX4_DEV_EVENT_PORT_UP:
if (p > ibdev->num_ports)
return;
return NOTIFY_DONE;
if (!mlx4_is_slave(dev) &&
rdma_port_get_link_layer(&ibdev->ib_dev, p) ==
IB_LINK_LAYER_INFINIBAND) {
@ -3220,7 +3255,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
case MLX4_DEV_EVENT_PORT_DOWN:
if (p > ibdev->num_ports)
return;
return NOTIFY_DONE;
ibev.event = IB_EVENT_PORT_ERR;
break;
@ -3233,7 +3268,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
case MLX4_DEV_EVENT_PORT_MGMT_CHANGE:
ew = kmalloc(sizeof *ew, GFP_ATOMIC);
if (!ew)
return;
return NOTIFY_DONE;
INIT_WORK(&ew->work, handle_port_mgmt_change_event);
memcpy(&ew->ib_eqe, eqe, sizeof *eqe);
@ -3243,7 +3278,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
queue_work(wq, &ew->work);
else
handle_port_mgmt_change_event(&ew->work);
return;
return NOTIFY_DONE;
case MLX4_DEV_EVENT_SLAVE_INIT:
/* here, p is the slave id */
@ -3259,7 +3294,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
1);
}
}
return;
return NOTIFY_DONE;
case MLX4_DEV_EVENT_SLAVE_SHUTDOWN:
if (mlx4_is_master(dev)) {
@ -3275,22 +3310,33 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr,
}
/* here, p is the slave id */
do_slave_init(ibdev, p, 0);
return;
return NOTIFY_DONE;
default:
return;
return NOTIFY_DONE;
}
ibev.device = ibdev_ptr;
ibev.device = &ibdev->ib_dev;
ibev.element.port_num = mlx4_is_bonded(ibdev->dev) ? 1 : (u8)p;
ib_dispatch_event(&ibev);
return NOTIFY_DONE;
}
static struct mlx4_interface mlx4_ib_interface = {
.add = mlx4_ib_add,
.remove = mlx4_ib_remove,
.event = mlx4_ib_event,
static const struct auxiliary_device_id mlx4_ib_id_table[] = {
{ .name = MLX4_ADEV_NAME ".ib" },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx4_ib_id_table);
static struct mlx4_adrv mlx4_ib_adrv = {
.adrv = {
.name = "ib",
.probe = mlx4_ib_probe,
.remove = mlx4_ib_remove,
.id_table = mlx4_ib_id_table,
},
.protocol = MLX4_PROT_IB_IPV6,
.flags = MLX4_INTFF_BONDING
};
@ -3315,7 +3361,7 @@ static int __init mlx4_ib_init(void)
if (err)
goto clean_cm;
err = mlx4_register_interface(&mlx4_ib_interface);
err = mlx4_register_auxiliary_driver(&mlx4_ib_adrv);
if (err)
goto clean_mcg;
@ -3337,7 +3383,7 @@ clean_qp_event:
static void __exit mlx4_ib_cleanup(void)
{
mlx4_unregister_interface(&mlx4_ib_interface);
mlx4_unregister_auxiliary_driver(&mlx4_ib_adrv);
mlx4_ib_mcg_destroy();
mlx4_ib_cm_destroy();
mlx4_ib_qp_event_cleanup();

View File

@ -38,6 +38,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/idr.h>
#include <linux/notifier.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_umem.h>
@ -644,6 +645,7 @@ struct mlx4_ib_dev {
spinlock_t reset_flow_resource_lock;
struct list_head qp_list;
struct mlx4_ib_diag_counters diag_counters[MLX4_DIAG_COUNTERS_TYPES];
struct notifier_block mlx_nb;
};
struct ib_event_work {

View File

@ -28,3 +28,4 @@ mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o \
fs.o \
qos.o \
std_types.o
mlx5_ib-$(CONFIG_MLX5_MACSEC) += macsec.o

View File

@ -993,7 +993,7 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
INIT_WORK(&cq->notify_work, notify_soft_wc_handler);
}
err = mlx5_vector2eqn(dev->mdev, vector, &eqn);
err = mlx5_comp_eqn_get(dev->mdev, vector, &eqn);
if (err)
goto err_cqb;

View File

@ -1002,7 +1002,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_QUERY_EQN)(
return PTR_ERR(c);
dev = to_mdev(c->ibucontext.device);
err = mlx5_vector2eqn(dev->mdev, user_vector, &dev_eqn);
err = mlx5_comp_eqn_get(dev->mdev, user_vector, &dev_eqn);
if (err < 0)
return err;

View File

@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */
#include "macsec.h"
#include <linux/mlx5/macsec.h>
struct mlx5_reserved_gids {
int macsec_index;
const struct ib_gid_attr *physical_gid;
};
struct mlx5_roce_gids {
struct list_head roce_gid_list_entry;
u16 gid_idx;
union {
struct sockaddr_in sockaddr_in;
struct sockaddr_in6 sockaddr_in6;
} addr;
};
struct mlx5_macsec_device {
struct list_head macsec_devices_list_entry;
void *macdev;
struct list_head macsec_roce_gids;
struct list_head tx_rules_list;
struct list_head rx_rules_list;
};
static void cleanup_macsec_device(struct mlx5_macsec_device *macsec_device)
{
if (!list_empty(&macsec_device->tx_rules_list) ||
!list_empty(&macsec_device->rx_rules_list) ||
!list_empty(&macsec_device->macsec_roce_gids))
return;
list_del(&macsec_device->macsec_devices_list_entry);
kfree(macsec_device);
}
static struct mlx5_macsec_device *get_macsec_device(void *macdev,
struct list_head *macsec_devices_list)
{
struct mlx5_macsec_device *iter, *macsec_device = NULL;
list_for_each_entry(iter, macsec_devices_list, macsec_devices_list_entry) {
if (iter->macdev == macdev) {
macsec_device = iter;
break;
}
}
if (macsec_device)
return macsec_device;
macsec_device = kzalloc(sizeof(*macsec_device), GFP_KERNEL);
if (!macsec_device)
return NULL;
macsec_device->macdev = macdev;
INIT_LIST_HEAD(&macsec_device->tx_rules_list);
INIT_LIST_HEAD(&macsec_device->rx_rules_list);
INIT_LIST_HEAD(&macsec_device->macsec_roce_gids);
list_add(&macsec_device->macsec_devices_list_entry, macsec_devices_list);
return macsec_device;
}
static void mlx5_macsec_del_roce_gid(struct mlx5_macsec_device *macsec_device, u16 gid_idx)
{
struct mlx5_roce_gids *current_gid, *next_gid;
list_for_each_entry_safe(current_gid, next_gid, &macsec_device->macsec_roce_gids,
roce_gid_list_entry)
if (current_gid->gid_idx == gid_idx) {
list_del(&current_gid->roce_gid_list_entry);
kfree(current_gid);
}
}
static void mlx5_macsec_save_roce_gid(struct mlx5_macsec_device *macsec_device,
const struct sockaddr *addr, u16 gid_idx)
{
struct mlx5_roce_gids *roce_gids;
roce_gids = kzalloc(sizeof(*roce_gids), GFP_KERNEL);
if (!roce_gids)
return;
roce_gids->gid_idx = gid_idx;
if (addr->sa_family == AF_INET)
memcpy(&roce_gids->addr.sockaddr_in, addr, sizeof(roce_gids->addr.sockaddr_in));
else
memcpy(&roce_gids->addr.sockaddr_in6, addr, sizeof(roce_gids->addr.sockaddr_in6));
list_add_tail(&roce_gids->roce_gid_list_entry, &macsec_device->macsec_roce_gids);
}
static void handle_macsec_gids(struct list_head *macsec_devices_list,
struct mlx5_macsec_event_data *data)
{
struct mlx5_macsec_device *macsec_device;
struct mlx5_roce_gids *gid;
macsec_device = get_macsec_device(data->macdev, macsec_devices_list);
if (!macsec_device)
return;
list_for_each_entry(gid, &macsec_device->macsec_roce_gids, roce_gid_list_entry) {
mlx5_macsec_add_roce_sa_rules(data->fs_id, (struct sockaddr *)&gid->addr,
gid->gid_idx, &macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, data->macsec_fs,
data->is_tx);
}
}
static void del_sa_roce_rule(struct list_head *macsec_devices_list,
struct mlx5_macsec_event_data *data)
{
struct mlx5_macsec_device *macsec_device;
macsec_device = get_macsec_device(data->macdev, macsec_devices_list);
WARN_ON(!macsec_device);
mlx5_macsec_del_roce_sa_rules(data->fs_id, data->macsec_fs,
&macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, data->is_tx);
}
static int macsec_event(struct notifier_block *nb, unsigned long event, void *data)
{
struct mlx5_macsec *macsec = container_of(nb, struct mlx5_macsec, blocking_events_nb);
mutex_lock(&macsec->lock);
switch (event) {
case MLX5_DRIVER_EVENT_MACSEC_SA_ADDED:
handle_macsec_gids(&macsec->macsec_devices_list, data);
break;
case MLX5_DRIVER_EVENT_MACSEC_SA_DELETED:
del_sa_roce_rule(&macsec->macsec_devices_list, data);
break;
default:
mutex_unlock(&macsec->lock);
return NOTIFY_DONE;
}
mutex_unlock(&macsec->lock);
return NOTIFY_OK;
}
void mlx5r_macsec_event_register(struct mlx5_ib_dev *dev)
{
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
dev->macsec.blocking_events_nb.notifier_call = macsec_event;
blocking_notifier_chain_register(&dev->mdev->macsec_nh,
&dev->macsec.blocking_events_nb);
}
void mlx5r_macsec_event_unregister(struct mlx5_ib_dev *dev)
{
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
blocking_notifier_chain_unregister(&dev->mdev->macsec_nh,
&dev->macsec.blocking_events_nb);
}
int mlx5r_macsec_init_gids_and_devlist(struct mlx5_ib_dev *dev)
{
int i, j, max_gids;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return 0;
}
max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size);
for (i = 0; i < dev->num_ports; i++) {
dev->port[i].reserved_gids = kcalloc(max_gids,
sizeof(*dev->port[i].reserved_gids),
GFP_KERNEL);
if (!dev->port[i].reserved_gids)
goto err;
for (j = 0; j < max_gids; j++)
dev->port[i].reserved_gids[j].macsec_index = -1;
}
INIT_LIST_HEAD(&dev->macsec.macsec_devices_list);
mutex_init(&dev->macsec.lock);
return 0;
err:
while (i >= 0) {
kfree(dev->port[i].reserved_gids);
i--;
}
return -ENOMEM;
}
void mlx5r_macsec_dealloc_gids(struct mlx5_ib_dev *dev)
{
int i;
if (!mlx5_is_macsec_roce_supported(dev->mdev))
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
for (i = 0; i < dev->num_ports; i++)
kfree(dev->port[i].reserved_gids);
mutex_destroy(&dev->macsec.lock);
}
int mlx5r_add_gid_macsec_operations(const struct ib_gid_attr *attr)
{
struct mlx5_ib_dev *dev = to_mdev(attr->device);
struct mlx5_macsec_device *macsec_device;
const struct ib_gid_attr *physical_gid;
struct mlx5_reserved_gids *mgids;
struct net_device *ndev;
int ret = 0;
union {
struct sockaddr_in sockaddr_in;
struct sockaddr_in6 sockaddr_in6;
} addr;
if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
return 0;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return 0;
}
rcu_read_lock();
ndev = rcu_dereference(attr->ndev);
if (!ndev) {
rcu_read_unlock();
return -ENODEV;
}
if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) {
rcu_read_unlock();
return 0;
}
dev_hold(ndev);
rcu_read_unlock();
mutex_lock(&dev->macsec.lock);
macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list);
if (!macsec_device) {
ret = -ENOMEM;
goto dev_err;
}
physical_gid = rdma_find_gid(attr->device, &attr->gid,
attr->gid_type, NULL);
if (!IS_ERR(physical_gid)) {
ret = set_roce_addr(to_mdev(physical_gid->device),
physical_gid->port_num,
physical_gid->index, NULL,
physical_gid);
if (ret)
goto gid_err;
mgids = &dev->port[attr->port_num - 1].reserved_gids[physical_gid->index];
mgids->macsec_index = attr->index;
mgids->physical_gid = physical_gid;
}
/* Proceed with adding steering rules, regardless if there was gid ambiguity or not.*/
rdma_gid2ip((struct sockaddr *)&addr, &attr->gid);
ret = mlx5_macsec_add_roce_rule(ndev, (struct sockaddr *)&addr, attr->index,
&macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, dev->mdev->macsec_fs);
if (ret && !IS_ERR(physical_gid))
goto rule_err;
mlx5_macsec_save_roce_gid(macsec_device, (struct sockaddr *)&addr, attr->index);
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
return ret;
rule_err:
set_roce_addr(to_mdev(physical_gid->device), physical_gid->port_num,
physical_gid->index, &physical_gid->gid, physical_gid);
mgids->macsec_index = -1;
gid_err:
rdma_put_gid_attr(physical_gid);
cleanup_macsec_device(macsec_device);
dev_err:
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
return ret;
}
void mlx5r_del_gid_macsec_operations(const struct ib_gid_attr *attr)
{
struct mlx5_ib_dev *dev = to_mdev(attr->device);
struct mlx5_macsec_device *macsec_device;
struct mlx5_reserved_gids *mgids;
struct net_device *ndev;
int i, max_gids;
if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
return;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
mgids = &dev->port[attr->port_num - 1].reserved_gids[attr->index];
if (mgids->macsec_index != -1) { /* Checking if physical gid has ambiguous IP */
rdma_put_gid_attr(mgids->physical_gid);
mgids->macsec_index = -1;
return;
}
rcu_read_lock();
ndev = rcu_dereference(attr->ndev);
if (!ndev) {
rcu_read_unlock();
return;
}
if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) {
rcu_read_unlock();
return;
}
dev_hold(ndev);
rcu_read_unlock();
mutex_lock(&dev->macsec.lock);
max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size);
for (i = 0; i < max_gids; i++) { /* Checking if macsec gid has ambiguous IP */
mgids = &dev->port[attr->port_num - 1].reserved_gids[i];
if (mgids->macsec_index == attr->index) {
const struct ib_gid_attr *physical_gid = mgids->physical_gid;
set_roce_addr(to_mdev(physical_gid->device),
physical_gid->port_num,
physical_gid->index,
&physical_gid->gid, physical_gid);
rdma_put_gid_attr(physical_gid);
mgids->macsec_index = -1;
break;
}
}
macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list);
mlx5_macsec_del_roce_rule(attr->index, dev->mdev->macsec_fs,
&macsec_device->tx_rules_list, &macsec_device->rx_rules_list);
mlx5_macsec_del_roce_gid(macsec_device, attr->index);
cleanup_macsec_device(macsec_device);
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
}

Some files were not shown because too many files have changed in this diff Show More