diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 809765bd9573..5d4180aaf18e 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -265,6 +265,71 @@ Description: Specific uncompressed frame descriptors bmCapabilities - still image support, fixed frame-rate support +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased +Date: Oct 2025 +KernelVersion: 5.4 +Description: Framebased format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name +Date: Oct 2025 +KernelVersion: 5.4 +Description: Specific framebased format descriptors + + ================== ======================================= + bFormatIndex unique id for this format descriptor; + only defined after parent header is + linked into the streaming class; + read-only + bmaControls this format's data for bmaControls in + the streaming header + bmInterlaceFlags specifies interlace information, + read-only + bAspectRatioY the X dimension of the picture aspect + ratio, read-only + bAspectRatioX the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex optimum frame index for this stream + bBitsPerPixel number of bits per pixel used to + specify color in the decoded video + frame + guidFormat globally unique id used to identify + stream-encoding format + ================== ======================================= + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased frame descriptors + + ========================= ===================================== + bFrameIndex unique id for this framedescriptor; + only defined after parent format is + linked into the streaming header; + read-only + dwFrameInterval indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval the frame interval the device would + like to use as default + dwBytesPerLine Specifies the number of bytes per line + + of video for packed fixed frame size + formats, allowing the receiver to + perform stride alignment of the video. + If the bVariableSize value (above) is + TRUE (1), or if the format does not + permit such alignment, this value shall + be set to zero (0). + dwMaxBitRate the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate the minimum bit rate at the longest + frame interval in bps + wHeight height of decoded bitmap frame in px + wWidth width of decoded bitmam frame in px + bmCapabilities still image support, fixed frame-rate + support + ========================= ===================================== + What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header Date: Dec 2014 KernelVersion: 4.0 diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 25443c227e27..c33eb805ec85 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3187,6 +3187,21 @@ in certain environments such as networked servers or real-time systems. + no_hash_pointers + Force pointers printed to the console or buffers to be + unhashed. By default, when a pointer is printed via %p + format string, that pointer is "hashed", i.e. obscured + by hashing the pointer value. This is a security feature + that hides actual kernel addresses from unprivileged + users, but it also makes debugging the kernel more + difficult since unequal pointers can no longer be + compared. However, if this command-line option is + specified, then all normal pointers will have their true + value printed. Pointers printed via %pK may still be + hashed. This option should only be specified when + debugging the kernel. Please do not use on production + kernels. + nohibernate [HIBERNATION] Disable hibernation and resume. nohz= [KNL] Boottime enable/disable dynamic ticks diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index b0522a4dd107..cdabec1e168c 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -21,6 +21,7 @@ whole; patches welcome! kasan ubsan kmemleak + kfence gdb-kernel-debugging kgdb kselftest diff --git a/Documentation/dev-tools/kfence.rst b/Documentation/dev-tools/kfence.rst new file mode 100644 index 000000000000..fdf04e741ea5 --- /dev/null +++ b/Documentation/dev-tools/kfence.rst @@ -0,0 +1,298 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. Copyright (C) 2020, Google LLC. + +Kernel Electric-Fence (KFENCE) +============================== + +Kernel Electric-Fence (KFENCE) is a low-overhead sampling-based memory safety +error detector. KFENCE detects heap out-of-bounds access, use-after-free, and +invalid-free errors. + +KFENCE is designed to be enabled in production kernels, and has near zero +performance overhead. Compared to KASAN, KFENCE trades performance for +precision. The main motivation behind KFENCE's design, is that with enough +total uptime KFENCE will detect bugs in code paths not typically exercised by +non-production test workloads. One way to quickly achieve a large enough total +uptime is when the tool is deployed across a large fleet of machines. + +Usage +----- + +To enable KFENCE, configure the kernel with:: + + CONFIG_KFENCE=y + +To build a kernel with KFENCE support, but disabled by default (to enable, set +``kfence.sample_interval`` to non-zero value), configure the kernel with:: + + CONFIG_KFENCE=y + CONFIG_KFENCE_SAMPLE_INTERVAL=0 + +KFENCE provides several other configuration options to customize behaviour (see +the respective help text in ``lib/Kconfig.kfence`` for more info). + +Tuning performance +~~~~~~~~~~~~~~~~~~ + +The most important parameter is KFENCE's sample interval, which can be set via +the kernel boot parameter ``kfence.sample_interval`` in milliseconds. The +sample interval determines the frequency with which heap allocations will be +guarded by KFENCE. The default is configurable via the Kconfig option +``CONFIG_KFENCE_SAMPLE_INTERVAL``. Setting ``kfence.sample_interval=0`` +disables KFENCE. + +The KFENCE memory pool is of fixed size, and if the pool is exhausted, no +further KFENCE allocations occur. With ``CONFIG_KFENCE_NUM_OBJECTS`` (default +255), the number of available guarded objects can be controlled. Each object +requires 2 pages, one for the object itself and the other one used as a guard +page; object pages are interleaved with guard pages, and every object page is +therefore surrounded by two guard pages. + +The total memory dedicated to the KFENCE memory pool can be computed as:: + + ( #objects + 1 ) * 2 * PAGE_SIZE + +Using the default config, and assuming a page size of 4 KiB, results in +dedicating 2 MiB to the KFENCE memory pool. + +Note: On architectures that support huge pages, KFENCE will ensure that the +pool is using pages of size ``PAGE_SIZE``. This will result in additional page +tables being allocated. + +Error reports +~~~~~~~~~~~~~ + +A typical out-of-bounds access looks like this:: + + ================================================================== + BUG: KFENCE: out-of-bounds read in test_out_of_bounds_read+0xa3/0x22b + + Out-of-bounds read at 0xffffffffb672efff (1B left of kfence-#17): + test_out_of_bounds_read+0xa3/0x22b + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + kfence-#17 [0xffffffffb672f000-0xffffffffb672f01f, size=32, cache=kmalloc-32] allocated by task 507: + test_alloc+0xf3/0x25b + test_out_of_bounds_read+0x98/0x22b + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + CPU: 4 PID: 107 Comm: kunit_try_catch Not tainted 5.8.0-rc6+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 + ================================================================== + +The header of the report provides a short summary of the function involved in +the access. It is followed by more detailed information about the access and +its origin. Note that, real kernel addresses are only shown when using the +kernel command line option ``no_hash_pointers``. + +Use-after-free accesses are reported as:: + + ================================================================== + BUG: KFENCE: use-after-free read in test_use_after_free_read+0xb3/0x143 + + Use-after-free read at 0xffffffffb673dfe0 (in kfence-#24): + test_use_after_free_read+0xb3/0x143 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + kfence-#24 [0xffffffffb673dfe0-0xffffffffb673dfff, size=32, cache=kmalloc-32] allocated by task 507: + test_alloc+0xf3/0x25b + test_use_after_free_read+0x76/0x143 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + freed by task 507: + test_use_after_free_read+0xa8/0x143 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + CPU: 4 PID: 109 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 + ================================================================== + +KFENCE also reports on invalid frees, such as double-frees:: + + ================================================================== + BUG: KFENCE: invalid free in test_double_free+0xdc/0x171 + + Invalid free of 0xffffffffb6741000: + test_double_free+0xdc/0x171 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + kfence-#26 [0xffffffffb6741000-0xffffffffb674101f, size=32, cache=kmalloc-32] allocated by task 507: + test_alloc+0xf3/0x25b + test_double_free+0x76/0x171 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + freed by task 507: + test_double_free+0xa8/0x171 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + CPU: 4 PID: 111 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 + ================================================================== + +KFENCE also uses pattern-based redzones on the other side of an object's guard +page, to detect out-of-bounds writes on the unprotected side of the object. +These are reported on frees:: + + ================================================================== + BUG: KFENCE: memory corruption in test_kmalloc_aligned_oob_write+0xef/0x184 + + Corrupted memory at 0xffffffffb6797ff9 [ 0xac . . . . . . ] (in kfence-#69): + test_kmalloc_aligned_oob_write+0xef/0x184 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + kfence-#69 [0xffffffffb6797fb0-0xffffffffb6797ff8, size=73, cache=kmalloc-96] allocated by task 507: + test_alloc+0xf3/0x25b + test_kmalloc_aligned_oob_write+0x57/0x184 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + CPU: 4 PID: 120 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 + ================================================================== + +For such errors, the address where the corruption occurred as well as the +invalidly written bytes (offset from the address) are shown; in this +representation, '.' denote untouched bytes. In the example above ``0xac`` is +the value written to the invalid address at offset 0, and the remaining '.' +denote that no following bytes have been touched. Note that, real values are +only shown if the kernel was booted with ``no_hash_pointers``; to avoid +information disclosure otherwise, '!' is used instead to denote invalidly +written bytes. + +And finally, KFENCE may also report on invalid accesses to any protected page +where it was not possible to determine an associated object, e.g. if adjacent +object pages had not yet been allocated:: + + ================================================================== + BUG: KFENCE: invalid read in test_invalid_access+0x26/0xe0 + + Invalid read at 0xffffffffb670b00a: + test_invalid_access+0x26/0xe0 + kunit_try_run_case+0x51/0x85 + kunit_generic_run_threadfn_adapter+0x16/0x30 + kthread+0x137/0x160 + ret_from_fork+0x22/0x30 + + CPU: 4 PID: 124 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014 + ================================================================== + +DebugFS interface +~~~~~~~~~~~~~~~~~ + +Some debugging information is exposed via debugfs: + +* The file ``/sys/kernel/debug/kfence/stats`` provides runtime statistics. + +* The file ``/sys/kernel/debug/kfence/objects`` provides a list of objects + allocated via KFENCE, including those already freed but protected. + +Implementation Details +---------------------- + +Guarded allocations are set up based on the sample interval. After expiration +of the sample interval, the next allocation through the main allocator (SLAB or +SLUB) returns a guarded allocation from the KFENCE object pool (allocation +sizes up to PAGE_SIZE are supported). At this point, the timer is reset, and +the next allocation is set up after the expiration of the interval. To "gate" a +KFENCE allocation through the main allocator's fast-path without overhead, +KFENCE relies on static branches via the static keys infrastructure. The static +branch is toggled to redirect the allocation to KFENCE. + +KFENCE objects each reside on a dedicated page, at either the left or right +page boundaries selected at random. The pages to the left and right of the +object page are "guard pages", whose attributes are changed to a protected +state, and cause page faults on any attempted access. Such page faults are then +intercepted by KFENCE, which handles the fault gracefully by reporting an +out-of-bounds access, and marking the page as accessible so that the faulting +code can (wrongly) continue executing (set ``panic_on_warn`` to panic instead). + +To detect out-of-bounds writes to memory within the object's page itself, +KFENCE also uses pattern-based redzones. For each object page, a redzone is set +up for all non-object memory. For typical alignments, the redzone is only +required on the unguarded side of an object. Because KFENCE must honor the +cache's requested alignment, special alignments may result in unprotected gaps +on either side of an object, all of which are redzoned. + +The following figure illustrates the page layout:: + + ---+-----------+-----------+-----------+-----------+-----------+--- + | xxxxxxxxx | O : | xxxxxxxxx | : O | xxxxxxxxx | + | xxxxxxxxx | B : | xxxxxxxxx | : B | xxxxxxxxx | + | x GUARD x | J : RED- | x GUARD x | RED- : J | x GUARD x | + | xxxxxxxxx | E : ZONE | xxxxxxxxx | ZONE : E | xxxxxxxxx | + | xxxxxxxxx | C : | xxxxxxxxx | : C | xxxxxxxxx | + | xxxxxxxxx | T : | xxxxxxxxx | : T | xxxxxxxxx | + ---+-----------+-----------+-----------+-----------+-----------+--- + +Upon deallocation of a KFENCE object, the object's page is again protected and +the object is marked as freed. Any further access to the object causes a fault +and KFENCE reports a use-after-free access. Freed objects are inserted at the +tail of KFENCE's freelist, so that the least recently freed objects are reused +first, and the chances of detecting use-after-frees of recently freed objects +is increased. + +Interface +--------- + +The following describes the functions which are used by allocators as well as +page handling code to set up and deal with KFENCE allocations. + +.. kernel-doc:: include/linux/kfence.h + :functions: is_kfence_address + kfence_shutdown_cache + kfence_alloc kfence_free __kfence_free + kfence_ksize kfence_object_start + kfence_handle_page_fault + +Related Tools +------------- + +In userspace, a similar approach is taken by `GWP-ASan +`_. GWP-ASan also relies on guard pages and +a sampling strategy to detect memory unsafety bugs at scale. KFENCE's design is +directly influenced by GWP-ASan, and can be seen as its kernel sibling. Another +similar but non-sampling approach, that also inspired the name "KFENCE", can be +found in the userspace `Electric Fence Malloc Debugger +`_. + +In the kernel, several tools exist to debug memory access errors, and in +particular KASAN can detect all bug classes that KFENCE can detect. While KASAN +is more precise, relying on compiler instrumentation, this comes at a +performance cost. + +It is worth highlighting that KASAN and KFENCE are complementary, with +different target environments. For instance, KASAN is the better debugging-aid, +where test cases or reproducers exists: due to the lower chance to detect the +error, it would require more effort using KFENCE to debug. Deployments at scale +that cannot afford to enable KASAN, however, would benefit from using KFENCE to +discover bugs due to code paths not exercised by test cases or fuzzers. diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 6882bfd2c097..8acb7244b2e6 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1823,18 +1823,20 @@ if precise results are needed. 3.8 /proc//fdinfo/ - Information about opened file --------------------------------------------------------------- This file provides information associated with an opened file. The regular -files have at least three fields -- 'pos', 'flags' and mnt_id. The 'pos' -represents the current offset of the opened file in decimal form [see lseek(2) -for details], 'flags' denotes the octal O_xxx mask the file has been -created with [see open(2) for details] and 'mnt_id' represents mount ID of -the file system containing the opened file [see 3.5 /proc//mountinfo -for details]. +files have at least four fields -- 'pos', 'flags', 'mnt_id' and 'ino'. +The 'pos' represents the current offset of the opened file in decimal +form [see lseek(2) for details], 'flags' denotes the octal O_xxx mask the +file has been created with [see open(2) for details] and 'mnt_id' represents +mount ID of the file system containing the opened file [see 3.5 +/proc//mountinfo for details]. 'ino' represents the inode number of +the file. A typical output is pos: 0 flags: 0100002 mnt_id: 19 + ino: 63107 All locks associated with a file descriptor are shown in its fdinfo too. @@ -1848,6 +1850,7 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 04002 mnt_id: 9 + ino: 63107 eventfd-count: 5a where 'eventfd-count' is hex value of a counter. @@ -1857,6 +1860,7 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 04002 mnt_id: 9 + ino: 63107 sigmask: 0000000000000200 where 'sigmask' is hex value of the signal mask associated @@ -1867,6 +1871,7 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 02 mnt_id: 9 + ino: 63107 tfd: 5 events: 1d data: ffffffffffffffff pos:0 ino:61af sdev:7 where 'tfd' is a target file descriptor number in decimal form, @@ -1883,6 +1888,8 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 02000000 + mnt_id: 9 + ino: 63107 inotify wd:3 ino:9e7e sdev:800013 mask:800afce ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:7e9e0000640d1b6d where 'wd' is a watch descriptor in decimal form, ie a target file @@ -1905,6 +1912,7 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 02 mnt_id: 9 + ino: 63107 fanotify flags:10 event-flags:0 fanotify mnt_id:12 mflags:40 mask:38 ignored_mask:40000003 fanotify ino:4f969 sdev:800013 mflags:0 mask:3b ignored_mask:40000000 fhandle-bytes:8 fhandle-type:1 f_handle:69f90400c275b5b4 @@ -1927,6 +1935,7 @@ pair provide additional information particular to the objects they represent. pos: 0 flags: 02 mnt_id: 9 + ino: 63107 clockid: 0 ticks: 0 settime flags: 01 @@ -1941,6 +1950,22 @@ pair provide additional information particular to the objects they represent. with TIMER_ABSTIME option which will be shown in 'settime flags', but 'it_value' still exhibits timer's remaining time. +DMA Buffer files +~~~~~~~~~~~~~~~~ + +:: + + pos: 0 + flags: 04002 + mnt_id: 9 + ino: 63107 + size: 32768 + count: 2 + exp_name: system-heap + +where 'size' is the size of the DMA buffer in bytes. 'count' is the file count of +the DMA buffer file. 'exp_name' is the name of the DMA buffer exporter. + 3.9 /proc//map_files - Information about memory mapped files --------------------------------------------------------------------- This directory contains symbolic links which represent memory mapped files diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 12cf648120f4..11ad3403bdda 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1660,6 +1660,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. diff --git a/Makefile b/Makefile index 4593e9d0cdde..b42946eb1203 100644 --- a/Makefile +++ b/Makefile @@ -596,9 +596,11 @@ CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%)) endif # CROSS_COMPILE ifeq ($(LLVM_IAS),0) -CLANG_FLAGS += -no-integrated-as +CLANG_FLAGS += -fno-integrated-as GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)elfedit)) CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE)) +else +CLANG_FLAGS += -fintegrated-as endif CLANG_FLAGS += -Werror=unknown-warning-option KBUILD_CPPFLAGS += $(CLANG_FLAGS) @@ -871,7 +873,8 @@ DEBUG_CFLAGS += -gsplit-dwarf else DEBUG_CFLAGS += -g endif -ifeq ($(LLVM_IAS),1) + +ifdef CONFIG_AS_IS_LLVM KBUILD_AFLAGS += -g else KBUILD_AFLAGS += -Wa,-gdwarf-2 diff --git a/android/GKI_VERSION b/android/GKI_VERSION index 6e522a6fd53c..eaa5b79f5d73 100644 --- a/android/GKI_VERSION +++ b/android/GKI_VERSION @@ -1 +1 @@ -LTS_5.4.289_4c8fb3275889 +LTS_5.4.302_91d385eb2a41 diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index de6c4df61082..d033d3f92d6d 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -124,6 +124,8 @@ #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_NETNS_COOKIE 71 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config index 2d12580404e8..0d6a262c17ea 100644 --- a/arch/arm/configs/vendor/sdxlemur.config +++ b/arch/arm/configs/vendor/sdxlemur.config @@ -51,6 +51,10 @@ CONFIG_CNSS_ASYNC=y CONFIG_CNSS_QCA6490=y CONFIG_CNSS_UTILS=y # CONFIG_CNSS_GENL is not set +CONFIG_CNSS=m +CONFIG_CNSS_CRYPTO=y +CONFIG_CNSS_PCI=y +CONFIG_CNSS_LOGGER=m CONFIG_QCOM_MEMORY_DUMP_V2=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index 68ca86f85eb7..580ed13b70a7 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild @@ -15,7 +15,6 @@ generic-y += mmiowb.h generic-y += msi.h generic-y += parport.h generic-y += preempt.h -generic-y += seccomp.h generic-y += serial.h generic-y += trace_clock.h diff --git a/arch/arm/include/asm/seccomp.h b/arch/arm/include/asm/seccomp.h new file mode 100644 index 000000000000..e9ad0f37d2ba --- /dev/null +++ b/arch/arm/include/asm/seccomp.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_SECCOMP_H +#define _ASM_SECCOMP_H + +#include + +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARM +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "arm" + +#endif /* _ASM_SECCOMP_H */ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 206e355cfa09..79f9288483f8 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -129,6 +129,7 @@ config ARM64 select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if !(ARM64_16K_PAGES && ARM64_VA_BITS_48) select HAVE_ARCH_KASAN_SW_TAGS if HAVE_ARCH_KASAN + select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB select HAVE_ARCH_MMAP_RND_BITS select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index 59ddabbd31fc..7b1714aaf30d 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -209,14 +209,27 @@ CONFIG_L2TP=y CONFIG_BRIDGE=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_NETEM=y CONFIG_NET_SCH_INGRESS=y CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y CONFIG_NET_CLS_BPF=y CONFIG_NET_CLS_MATCHALL=y CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y CONFIG_NET_ACT_BPF=y CONFIG_BPF_JIT=y CONFIG_BT=y @@ -589,6 +602,9 @@ CONFIG_DEBUG_INFO_DWARF4=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_KFENCE=y +CONFIG_KFENCE_SAMPLE_INTERVAL=500 +CONFIG_KFENCE_NUM_OBJECTS=63 CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set CONFIG_PANIC_ON_OOPS=y diff --git a/arch/arm64/include/asm/kfence.h b/arch/arm64/include/asm/kfence.h new file mode 100644 index 000000000000..d061176d57ea --- /dev/null +++ b/arch/arm64/include/asm/kfence.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * arm64 KFENCE support. + * + * Copyright (C) 2020, Google LLC. + */ + +#ifndef __ASM_KFENCE_H +#define __ASM_KFENCE_H + +#include + +static inline bool arch_kfence_init_pool(void) { return true; } + +static inline bool kfence_protect_page(unsigned long addr, bool protect) +{ + set_memory_valid(addr, 1, !protect); + + return true; +} + +#endif /* __ASM_KFENCE_H */ diff --git a/arch/arm64/include/asm/seccomp.h b/arch/arm64/include/asm/seccomp.h index c36387170936..30256233788b 100644 --- a/arch/arm64/include/asm/seccomp.h +++ b/arch/arm64/include/asm/seccomp.h @@ -19,4 +19,13 @@ #include +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_AARCH64 +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "aarch64" +#ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_ARM +# define SECCOMP_ARCH_COMPAT_NR __NR_compat_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "arm" +#endif + #endif /* _ASM_SECCOMP_H */ diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 8fcd1dbe1fb7..ae412b438122 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, } else if (addr < PAGE_SIZE) { msg = "NULL pointer dereference"; } else { + if (kfence_handle_page_fault(addr, esr & ESR_ELx_WNR, regs)) + return; + msg = "paging request"; } diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 9eaaa69fea9a..b6983b5502bc 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -471,7 +471,8 @@ static void __init map_mem(pgd_t *pgdp) struct memblock_region *reg; int flags = 0; - if (rodata_full || debug_pagealloc_enabled()) + if (rodata_full || debug_pagealloc_enabled() || + IS_ENABLED(CONFIG_KFENCE)) flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; /* @@ -1470,7 +1471,12 @@ int arch_add_memory(int nid, u64 start, u64 size, return -1; } - if (rodata_full || debug_pagealloc_enabled()) + /* + * KFENCE requires linear map to be mapped at page granularity, so that + * it is possible to protect/unprotect single pages in the KFENCE pool. + */ + if (rodata_full || debug_pagealloc_enabled() || + IS_ENABLED(CONFIG_KFENCE)) flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start), diff --git a/arch/csky/include/asm/seccomp.h b/arch/csky/include/asm/seccomp.h new file mode 100644 index 000000000000..d33e758126fb --- /dev/null +++ b/arch/csky/include/asm/seccomp.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_SECCOMP_H +#define _ASM_SECCOMP_H + +#include + +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_CSKY +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "csky" + +#endif /* _ASM_SECCOMP_H */ diff --git a/arch/mips/include/asm/seccomp.h b/arch/mips/include/asm/seccomp.h index e383d7e27b93..aa809589a181 100644 --- a/arch/mips/include/asm/seccomp.h +++ b/arch/mips/include/asm/seccomp.h @@ -9,12 +9,12 @@ static inline const int *get_compat_mode1_syscalls(void) static const int syscalls_O32[] = { __NR_O32_Linux + 3, __NR_O32_Linux + 4, __NR_O32_Linux + 1, __NR_O32_Linux + 193, - 0, /* null terminated */ + -1, /* negative terminated */ }; static const int syscalls_N32[] = { __NR_N32_Linux + 0, __NR_N32_Linux + 1, __NR_N32_Linux + 58, __NR_N32_Linux + 211, - 0, /* null terminated */ + -1, /* negative terminated */ }; if (IS_ENABLED(CONFIG_MIPS32_O32) && test_thread_flag(TIF_32BIT_REGS)) diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index d0a9ed2ca2d6..ff3ab771e769 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -135,6 +135,8 @@ #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_NETNS_COOKIE 71 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild index 9ceedf6393c4..ae63364de2cf 100644 --- a/arch/parisc/include/asm/Kbuild +++ b/arch/parisc/include/asm/Kbuild @@ -19,7 +19,6 @@ generic-y += mm-arch-hooks.h generic-y += mmiowb.h generic-y += percpu.h generic-y += preempt.h -generic-y += seccomp.h generic-y += trace_clock.h generic-y += user.h generic-y += vga.h diff --git a/arch/parisc/include/asm/seccomp.h b/arch/parisc/include/asm/seccomp.h new file mode 100644 index 000000000000..b058b2220322 --- /dev/null +++ b/arch/parisc/include/asm/seccomp.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_SECCOMP_H +#define _ASM_SECCOMP_H + +#include + +#ifdef CONFIG_64BIT +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PARISC64 +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "parisc64" +# ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_PARISC +# define SECCOMP_ARCH_COMPAT_NR NR_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "parisc" +# endif +#else /* !CONFIG_64BIT */ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PARISC +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "parisc" +#endif + +#endif /* _ASM_SECCOMP_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 10173c32195e..1a8ec3838c9b 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -116,6 +116,8 @@ #define SO_DETACH_REUSEPORT_BPF 0x4042 +#define SO_NETNS_COOKIE 0x4045 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 diff --git a/arch/powerpc/include/asm/seccomp.h b/arch/powerpc/include/asm/seccomp.h index 51209f6071c5..ac2033f134f0 100644 --- a/arch/powerpc/include/asm/seccomp.h +++ b/arch/powerpc/include/asm/seccomp.h @@ -8,4 +8,27 @@ #include +#ifdef __LITTLE_ENDIAN__ +#define __SECCOMP_ARCH_LE __AUDIT_ARCH_LE +#define __SECCOMP_ARCH_LE_NAME "le" +#else +#define __SECCOMP_ARCH_LE 0 +#define __SECCOMP_ARCH_LE_NAME +#endif + +#ifdef CONFIG_PPC64 +# define SECCOMP_ARCH_NATIVE (AUDIT_ARCH_PPC64 | __SECCOMP_ARCH_LE) +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "ppc64" __SECCOMP_ARCH_LE_NAME +# ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT (AUDIT_ARCH_PPC | __SECCOMP_ARCH_LE) +# define SECCOMP_ARCH_COMPAT_NR NR_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "ppc" __SECCOMP_ARCH_LE_NAME +# endif +#else /* !CONFIG_PPC64 */ +# define SECCOMP_ARCH_NATIVE (AUDIT_ARCH_PPC | __SECCOMP_ARCH_LE) +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "ppc" __SECCOMP_ARCH_LE_NAME +#endif + #endif /* _ASM_POWERPC_SECCOMP_H */ diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 1641d8201412..054ab2f280db 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -38,7 +38,7 @@ ifeq ($(CONFIG_LD_IS_LLD),y) ifeq ($(shell test $(CONFIG_LLD_VERSION) -lt 150000; echo $$?),0) KBUILD_CFLAGS += -mno-relax KBUILD_AFLAGS += -mno-relax -ifneq ($(LLVM_IAS),1) +ifndef CONFIG_AS_IS_LLVM KBUILD_CFLAGS += -Wa,-mno-relax KBUILD_AFLAGS += -Wa,-mno-relax endif diff --git a/arch/s390/include/asm/seccomp.h b/arch/s390/include/asm/seccomp.h index 795bbe0d7ca6..71d46f0ba97b 100644 --- a/arch/s390/include/asm/seccomp.h +++ b/arch/s390/include/asm/seccomp.h @@ -16,4 +16,13 @@ #include +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390X +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "s390x" +#ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_S390 +# define SECCOMP_ARCH_COMPAT_NR NR_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "s390" +#endif + #endif /* _ASM_S390_SECCOMP_H */ diff --git a/arch/sh/include/asm/seccomp.h b/arch/sh/include/asm/seccomp.h index 54111e4d32b8..d4578395fd66 100644 --- a/arch/sh/include/asm/seccomp.h +++ b/arch/sh/include/asm/seccomp.h @@ -8,4 +8,14 @@ #define __NR_seccomp_exit __NR_exit #define __NR_seccomp_sigreturn __NR_rt_sigreturn +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define __SECCOMP_ARCH_LE __AUDIT_ARCH_LE +#else +#define __SECCOMP_ARCH_LE 0 +#endif + +#define SECCOMP_ARCH_NATIVE (AUDIT_ARCH_SH | __SECCOMP_ARCH_LE) +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "sh" + #endif /* __ASM_SECCOMP_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 8029b681fc7c..08f9bbbf5bf2 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -117,6 +117,8 @@ #define SO_DETACH_REUSEPORT_BPF 0x0047 +#define SO_NETNS_COOKIE 0x0050 + #if !defined(__KERNEL__) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6e1e5f49664e..8a42463ffee5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -139,6 +139,7 @@ config X86 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if X86_64 + select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig index 5e751e6cb304..5f88798bb496 100644 --- a/arch/x86/configs/gki_defconfig +++ b/arch/x86/configs/gki_defconfig @@ -188,14 +188,27 @@ CONFIG_L2TP=y CONFIG_BRIDGE=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_NETEM=y CONFIG_NET_SCH_INGRESS=y CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y CONFIG_NET_CLS_BPF=y CONFIG_NET_CLS_MATCHALL=y CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y CONFIG_NET_ACT_BPF=y CONFIG_BPF_JIT=y CONFIG_BT=y @@ -520,6 +533,9 @@ CONFIG_DEBUG_INFO_DWARF4=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_KFENCE=y +CONFIG_KFENCE_SAMPLE_INTERVAL=500 +CONFIG_KFENCE_NUM_OBJECTS=63 CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set CONFIG_PANIC_ON_OOPS=y diff --git a/arch/x86/include/asm/kfence.h b/arch/x86/include/asm/kfence.h new file mode 100644 index 000000000000..2d66d70600bd --- /dev/null +++ b/arch/x86/include/asm/kfence.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * x86 KFENCE support. + * + * Copyright (C) 2020, Google LLC. + */ + +#ifndef _ASM_X86_KFENCE_H +#define _ASM_X86_KFENCE_H + +#include +#include + +#include +#include +#include +#include + +/* Force 4K pages for __kfence_pool. */ +static inline bool arch_kfence_init_pool(void) +{ + unsigned long addr; + + for (addr = (unsigned long)__kfence_pool; is_kfence_address((void *)addr); + addr += PAGE_SIZE) { + unsigned int level; + + if (!lookup_address(addr, &level)) + return false; + + if (level != PG_LEVEL_4K) + set_memory_4k(addr, 1); + } + + return true; +} + +/* Protect the given page and flush TLB. */ +static inline bool kfence_protect_page(unsigned long addr, bool protect) +{ + unsigned int level; + pte_t *pte = lookup_address(addr, &level); + + if (WARN_ON(!pte || level != PG_LEVEL_4K)) + return false; + + /* + * We need to avoid IPIs, as we may get KFENCE allocations or faults + * with interrupts disabled. Therefore, the below is best-effort, and + * does not flush TLBs on all CPUs. We can tolerate some inaccuracy; + * lazy fault handling takes care of faults after the page is PRESENT. + */ + + if (protect) + set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); + else + set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); + + /* Flush this CPU's TLB. */ + __flush_tlb_one_kernel(addr); + return true; +} + +#endif /* _ASM_X86_KFENCE_H */ diff --git a/arch/x86/include/asm/seccomp.h b/arch/x86/include/asm/seccomp.h index 2bd1338de236..fef16e398161 100644 --- a/arch/x86/include/asm/seccomp.h +++ b/arch/x86/include/asm/seccomp.h @@ -16,6 +16,26 @@ #define __NR_seccomp_sigreturn_32 __NR_ia32_sigreturn #endif +#ifdef CONFIG_X86_64 +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_X86_64 +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "x86_64" +# ifdef CONFIG_COMPAT +# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_I386 +# define SECCOMP_ARCH_COMPAT_NR IA32_NR_syscalls +# define SECCOMP_ARCH_COMPAT_NAME "ia32" +# endif +/* + * x32 will have __X32_SYSCALL_BIT set in syscall number. We don't support + * caching them and they are treated as out of range syscalls, which will + * always pass through the BPF filter. + */ +#else /* !CONFIG_X86_64 */ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_I386 +# define SECCOMP_ARCH_NATIVE_NR NR_syscalls +# define SECCOMP_ARCH_NATIVE_NAME "ia32" +#endif + #include #endif /* _ASM_X86_SECCOMP_H */ diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 2fa5e0e5f8e5..e6dc014ece01 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -9,6 +9,7 @@ #include /* oops_begin/end, ... */ #include /* search_exception_tables */ #include /* max_low_pfn */ +#include /* kfence_handle_page_fault */ #include /* NOKPROBE_SYMBOL, ... */ #include /* kmmio_handler, ... */ #include /* perf_sw_event */ @@ -801,6 +802,11 @@ no_context(struct pt_regs *regs, unsigned long error_code, if (IS_ENABLED(CONFIG_EFI)) efi_recover_from_page_fault(address); + /* Only not-present faults should be handled by KFENCE. */ + if (!(error_code & X86_PF_PROT) && + kfence_handle_page_fault(address, error_code & X86_PF_WRITE, regs)) + return; + oops: /* * Oops. The kernel tried to access some bad page. We'll have to diff --git a/arch/xtensa/include/asm/seccomp.h b/arch/xtensa/include/asm/seccomp.h new file mode 100644 index 000000000000..f1cb6b0a9e1f --- /dev/null +++ b/arch/xtensa/include/asm/seccomp.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_SECCOMP_H +#define _ASM_SECCOMP_H + +#include + +#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_XTENSA +#define SECCOMP_ARCH_NATIVE_NR NR_syscalls +#define SECCOMP_ARCH_NATIVE_NAME "xtensa" + +#endif /* _ASM_SECCOMP_H */ diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 7ecf16753f17..e5079c9144d9 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ /* Uncomment this block to log an error on every VERIFY failure */ @@ -62,6 +62,7 @@ #define TZ_PIL_AUTH_QDSP6_PROC 1 #define FASTRPC_DMAHANDLE_NOMAP (16) +#define FASTRPC_MAP_DMA_HANDLE 0x20000 #define FASTRPC_ENOSUCH 39 #define DEBUGFS_SIZE 3072 @@ -1128,7 +1129,7 @@ static void fastrpc_mmap_add(struct fastrpc_mmap *map) } static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd, - uintptr_t va, size_t len, int mflags, int refs, + uintptr_t va, size_t len, int mflags, bool refs, struct fastrpc_mmap **ppmap) { struct fastrpc_mmap *match = NULL, *map = NULL; @@ -1306,7 +1307,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags) dma_free_attrs(me->dev, map->size, (void *)map->va, (dma_addr_t)map->phys, (unsigned long)map->attr); } - } else if (map->flags == FASTRPC_DMAHANDLE_NOMAP) { + } else if (map->flags & FASTRPC_DMAHANDLE_NOMAP) { trace_fastrpc_dma_unmap(cid, map->phys, map->size); if (!IS_ERR_OR_NULL(map->table)) dma_buf_unmap_attachment(map->attach, map->table, @@ -1391,6 +1392,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned long flags; int err = 0, vmid, sgl_index = 0; struct scatterlist *sgl = NULL; + bool take_ref = true; if (!fl) { err = -EBADF; @@ -1404,7 +1406,9 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, } chan = &apps->channel[cid]; - if (!fastrpc_mmap_find(fl, fd, va, len, mflags, 1, ppmap)) + if (mflags & FASTRPC_MAP_DMA_HANDLE) + take_ref = false; + if (!fastrpc_mmap_find(fl, fd, va, len, mflags, take_ref, ppmap)) return 0; map = kzalloc(sizeof(*map), GFP_KERNEL); VERIFY(err, !IS_ERR_OR_NULL(map)); @@ -1442,7 +1446,7 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, if (err) goto bail; } - } else if (mflags == FASTRPC_DMAHANDLE_NOMAP) { + } else if (mflags & FASTRPC_DMAHANDLE_NOMAP) { if (map->attr & FASTRPC_ATTR_KEEP_MAP) { ADSPRPC_ERR("Invalid attribute 0x%x for fd %d\n", map->attr, fd); @@ -2502,10 +2506,10 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc); mutex_lock(&ctx->fl->map_mutex); for (i = bufs; i < bufs + handles; i++) { - int dmaflags = 0; + int dmaflags = FASTRPC_MAP_DMA_HANDLE; if (ctx->attrs && (ctx->attrs[i] & FASTRPC_ATTR_NOMAP)) - dmaflags = FASTRPC_DMAHANDLE_NOMAP; + dmaflags |= FASTRPC_DMAHANDLE_NOMAP; if (ctx->fds && (ctx->fds[i] != -1)) err = fastrpc_mmap_create(ctx->fl, ctx->fds[i], FASTRPC_ATTR_NOVA, 0, 0, dmaflags, @@ -2666,7 +2670,7 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx) if (ctx->maps[i]) { /* check if map still exist */ if (!fastrpc_mmap_find(ctx->fl, ctx->fds[i], 0, 0, - 0, 0, &mmap)) { + 0, false, &mmap)) { if (mmap) { pages[i].addr = mmap->phys; pages[i].size = mmap->size; @@ -2881,7 +2885,7 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx, if (!fdlist[i]) break; if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], 0, 0, - 0, 0, &mmap)) { + 0, false, &mmap)) { if (mmap && mmap->dma_handle_refs) { mmap->dma_handle_refs = 0; fastrpc_mmap_free(mmap, 0); @@ -4961,7 +4965,7 @@ static int fastrpc_internal_munmap_fd(struct fastrpc_file *fl, } mutex_lock(&fl->internal_map_mutex); mutex_lock(&fl->map_mutex); - err = fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map); + err = fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, false, &map); if (err) { ADSPRPC_ERR( "mapping not found to unmap fd 0x%x, va 0x%llx, len 0x%x, err %d\n", diff --git a/drivers/char/random.c b/drivers/char/random.c index 693640de7271..e7fc274a9608 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -799,16 +799,11 @@ early_param("random.trust_cpu", parse_trust_cpu); early_param("random.trust_bootloader", parse_trust_bootloader); /* - * The first collection of entropy occurs at system boot while interrupts - * are still turned off. Here we push in latent entropy, RDSEED, a timestamp, - * utsname(), and the command line. Depending on the above configuration knob, - * RDSEED may be considered sufficient for initialization. Note that much - * earlier setup may already have pushed entropy into the input pool by the - * time we get here. + * This is called extremely early, before time keeping functionality is + * available, but arch randomness is. Interrupts are not yet enabled. */ -int __init random_init(const char *command_line) +void __init random_init_early(const char *command_line) { - ktime_t now = ktime_get_real(); unsigned int i, arch_bits; unsigned long entropy; @@ -821,22 +816,41 @@ int __init random_init(const char *command_line) i < BLAKE2S_BLOCK_SIZE; i += sizeof(entropy)) { if (!arch_get_random_seed_long_early(&entropy) && !arch_get_random_long_early(&entropy)) { - entropy = random_get_entropy(); arch_bits -= sizeof(entropy) * 8; + continue; } _mix_pool_bytes(&entropy, sizeof(entropy)); } - _mix_pool_bytes(&now, sizeof(now)); - _mix_pool_bytes(utsname(), sizeof(*(utsname()))); - _mix_pool_bytes(command_line, strlen(command_line)); - add_latent_entropy(); + _mix_pool_bytes(command_line, strlen(command_line)); + + /* Reseed if already seeded by earlier phases. */ if (crng_ready()) crng_reseed(); else if (trust_cpu) _credit_init_bits(arch_bits); +} - return 0; +/* + * This is called a little bit after the prior function, and now there is + * access to timestamps counters. Interrupts are not yet enabled. + */ +void __init random_init(void) +{ + unsigned long entropy = random_get_entropy(); + ktime_t now = ktime_get_real(); + + _mix_pool_bytes(utsname(), sizeof(*(utsname()))); + _mix_pool_bytes(&now, sizeof(now)); + _mix_pool_bytes(&entropy, sizeof(entropy)); + add_latent_entropy(); + + /* Reseed if already seeded by earlier phases. */ + if (crng_ready()) + crng_reseed(); + + WARN(!entropy, "Missing cycle counter and fallback timer; RNG " + "entropy collection will consequently suffer."); } /* diff --git a/drivers/crypto/msm/qcedev_smmu.c b/drivers/crypto/msm/qcedev_smmu.c index 8d4844becc85..7039bce67c5c 100644 --- a/drivers/crypto/msm/qcedev_smmu.c +++ b/drivers/crypto/msm/qcedev_smmu.c @@ -329,10 +329,6 @@ int qcedev_check_and_map_buffer(void *handle, mapped_size = binfo->ion_buf.mapped_buf_size; atomic_inc(&binfo->ref_count); - /* Add buffer mapping information to regd buffer list */ - mutex_lock(&qce_hndl->registeredbufs.lock); - list_add_tail(&binfo->list, &qce_hndl->registeredbufs.list); - mutex_unlock(&qce_hndl->registeredbufs.lock); } /* Make sure the offset is within the mapped range */ @@ -344,6 +340,13 @@ int qcedev_check_and_map_buffer(void *handle, goto unmap; } + if (!found) { + /* Add buffer mapping information to regd buffer list */ + mutex_lock(&qce_hndl->registeredbufs.lock); + list_add_tail(&binfo->list, &qce_hndl->registeredbufs.list); + mutex_unlock(&qce_hndl->registeredbufs.lock); + } + /* return the mapped virtual address adjusted by offset */ *vaddr += offset; @@ -352,9 +355,6 @@ int qcedev_check_and_map_buffer(void *handle, unmap: if (!found) { qcedev_unmap_buffer(handle, mem_client, binfo); - mutex_lock(&qce_hndl->registeredbufs.lock); - list_del(&binfo->list); - mutex_unlock(&qce_hndl->registeredbufs.lock); } error: diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 668477348ae9..f7be1c0353d2 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -882,7 +882,7 @@ int __suspend_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) struct bwmon *m = to_bwmon(hw); mon_irq_disable(m, type); - free_irq(m->irq, m); + disable_irq(m->irq); mon_disable(m, type); mon_irq_clear(m, type); @@ -908,7 +908,6 @@ static __always_inline int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) { struct bwmon *m = to_bwmon(hw); - int ret; irq_handler_t handler; switch (type) { @@ -924,15 +923,7 @@ int __resume_bw_hwmon(struct bw_hwmon *hw, enum mon_reg_type type) } mon_clear(m, false, type); - ret = request_threaded_irq(m->irq, handler, bwmon_intr_thread, - IRQF_ONESHOT | IRQF_SHARED, - dev_name(m->dev), m); - if (ret < 0) { - dev_err(m->dev, "Unable to register interrupt handler! (%d)\n", - ret); - return ret; - } - + enable_irq(m->irq); mon_irq_enable(m, type); mon_enable(m, type); diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index 44e2e7de129e..eb9aea7851ff 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2524,6 +2525,12 @@ struct dma_async_tx_descriptor *gpi_prep_slave_sg(struct dma_chan *chan, for_each_sg(sgl, sg, sg_len, i) { tre = sg_virt(sg); + if (!tre) { + kfree(gpi_desc); + GPII_ERR(gpii, gpii_chan->chid, "TRE address is null\n"); + return NULL; + } + if (sg_len == 1) { tre_type = MSM_GPI_TRE_TYPE(((struct msm_gpi_tre *)tre)); diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 048e33de599c..93bebbdb24fc 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -2523,6 +2524,7 @@ int a6xx_perfcounter_update(struct adreno_device *adreno_dev, struct cpu_gpu_lock *lock = ptr; u32 *data = ptr + sizeof(*lock); int i, offset = 0; + u32 pending_pairs = 2; /* No of pairs to add: and */ if (cpu_gpu_lock(lock)) { cpu_gpu_unlock(lock); @@ -2546,6 +2548,13 @@ int a6xx_perfcounter_update(struct adreno_device *adreno_dev, offset += 2; } + /* Ensure there is enough space in the reglist buffer for new pairs */ + if ((offset + (pending_pairs * 2)) >= + (adreno_dev->pwrup_reglist->size / sizeof(u32))) { + cpu_gpu_unlock(lock); + return -ENOSPC; + } + /* * For all targets A6XX_RBBM_PERFCTR_CNTL needs to be the last entry, * so overwrite the existing A6XX_RBBM_PERFCNTL_CTRL and add it back to diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c index 8a4ea1752b24..07b1e1d24f45 100644 --- a/drivers/gpu/msm/adreno_a6xx_gmu.c +++ b/drivers/gpu/msm/adreno_a6xx_gmu.c @@ -593,7 +593,7 @@ static int find_vma_block(struct a6xx_gmu_device *gmu, u32 addr, u32 size) { int i; - for (i = 0; i < GMU_MEM_TYPE_MAX; i++) { + for (i = 0; i < gmu->num_vmas; i++) { struct gmu_vma_entry *vma = &gmu->vma[i]; if ((addr >= vma->start) && @@ -2685,10 +2685,13 @@ int a6xx_gmu_probe(struct kgsl_device *device, if (ret) goto error; - if (adreno_is_a650_family(adreno_dev)) + if (adreno_is_a650_family(adreno_dev)) { gmu->vma = a6xx_gmu_vma; - else + gmu->num_vmas = ARRAY_SIZE(a6xx_gmu_vma); + } else { gmu->vma = a6xx_gmu_vma_legacy; + gmu->num_vmas = ARRAY_SIZE(a6xx_gmu_vma_legacy); + } /* Map and reserve GMU CSRs registers */ ret = a6xx_gmu_reg_probe(adreno_dev); diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.h b/drivers/gpu/msm/adreno_a6xx_gmu.h index d39597683ec9..b671771c46b4 100644 --- a/drivers/gpu/msm/adreno_a6xx_gmu.h +++ b/drivers/gpu/msm/adreno_a6xx_gmu.h @@ -187,6 +187,8 @@ struct a6xx_gmu_device { /** @global_entries: To keep track of number of gmu buffers */ u32 global_entries; struct gmu_vma_entry *vma; + /** @num_vmas: Number of entries in the @vma array */ + u32 num_vmas; unsigned int log_wptr_retention; /** @cm3_fault: whether gmu received a cm3 fault interrupt */ atomic_t cm3_fault; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 60368d1ffcba..0699fdadade3 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -355,6 +355,7 @@ static void kgsl_destroy_ion(struct kgsl_memdesc *memdesc) } memdesc->sgt = NULL; + entry->priv_data = NULL; } static const struct kgsl_memdesc_ops kgsl_dmabuf_ops = { @@ -4054,9 +4055,9 @@ static unsigned long _gpu_set_svm_region(struct kgsl_process_private *private, return addr; } -static unsigned long get_align(struct kgsl_mem_entry *entry) +unsigned long kgsl_get_align(struct kgsl_memdesc *memdesc) { - int bit = kgsl_memdesc_get_align(&entry->memdesc); + u32 bit = kgsl_memdesc_get_align(memdesc); if (bit >= ilog2(SZ_2M)) return SZ_2M; @@ -4065,7 +4066,7 @@ static unsigned long get_align(struct kgsl_mem_entry *entry) else if (bit >= ilog2(SZ_64K)) return SZ_64K; - return SZ_4K; + return PAGE_SIZE; } static unsigned long set_svm_area(struct file *file, @@ -4098,7 +4099,7 @@ static unsigned long get_svm_unmapped_area(struct file *file, { struct kgsl_device_private *dev_priv = file->private_data; struct kgsl_process_private *private = dev_priv->process_priv; - unsigned long align = get_align(entry); + unsigned long align = kgsl_get_align(&entry->memdesc); unsigned long ret, iova; u64 start = 0, end = 0; struct vm_area_struct *vma; diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 0f0721522574..4698aacf94cf 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2008-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef __KGSL_H #define __KGSL_H @@ -473,6 +473,8 @@ void kgsl_mmu_remove_global(struct kgsl_device *device, struct kgsl_memdesc *memdesc); /* Helper functions */ +unsigned long kgsl_get_align(struct kgsl_memdesc *memdesc); + int kgsl_request_irq(struct platform_device *pdev, const char *name, irq_handler_t handler, void *data); diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index a90636a16f82..1298f6247c9e 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2002,2008-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -146,7 +147,7 @@ static const char *memtype_str(int memtype) static char get_alignflag(const struct kgsl_memdesc *m) { - int align = kgsl_memdesc_get_align(m); + u32 align = kgsl_memdesc_get_align(m); if (align >= ilog2(SZ_1M)) return 'L'; diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 690ffe008811..25a5750e452a 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -2229,8 +2229,7 @@ static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable, size = kgsl_memdesc_footprint(memdesc); - align = max_t(uint64_t, 1 << kgsl_memdesc_get_align(memdesc), - PAGE_SIZE); + align = kgsl_get_align(memdesc); if (memdesc->flags & KGSL_MEMFLAGS_FORCE_32BIT) { start = pt->compat_va_start; diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 389fd86078ff..1f55ffcfb265 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2002,2007-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef __KGSL_SHAREDMEM_H #define __KGSL_SHAREDMEM_H @@ -157,7 +158,7 @@ void kgsl_free_globals(struct kgsl_device *device); * * Returns the alignment requested, as power of 2 exponent. */ -static inline int +static inline u32 kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc) { return MEMFLAGS(memdesc->flags, KGSL_MEMALIGN_MASK, diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c index 3dbaef99d89c..947ba3ca140d 100644 --- a/drivers/iommu/dma-mapping-fast.c +++ b/drivers/iommu/dma-mapping-fast.c @@ -244,8 +244,12 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova, } spin_lock_irqsave(&mapping->lock, flags); - av8l_fast_unmap_public(mapping->pgtbl_ops, iova, len); + + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, iova, len))) + goto fail; + __fast_smmu_free_iova(mapping, iova, len); +fail: spin_unlock_irqrestore(&mapping->lock, flags); trace_unmap(to_msm_iommu_domain(mapping->domain), iova - offset, len, @@ -385,7 +389,8 @@ static void fast_smmu_unmap_sg(struct device *dev, len = ALIGN(sg_dma_address(sg) + sg_dma_len(sg) - (start - offset), FAST_PAGE_SIZE); - av8l_fast_unmap_public(mapping->pgtbl_ops, start, len); + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, start, len))) + return; spin_lock_irqsave(&mapping->lock, flags); __fast_smmu_free_iova(mapping, start, len); @@ -653,7 +658,10 @@ static void fast_smmu_free(struct device *dev, size_t size, size = ALIGN(size, FAST_PAGE_SIZE); spin_lock_irqsave(&mapping->lock, flags); - av8l_fast_unmap_public(mapping->pgtbl_ops, dma_handle, size); + + if (unlikely(!av8l_fast_unmap_public(mapping->pgtbl_ops, dma_handle, size))) + goto fail; + __fast_smmu_free_iova(mapping, dma_handle, size); spin_unlock_irqrestore(&mapping->lock, flags); @@ -674,6 +682,11 @@ static void fast_smmu_free(struct device *dev, size_t size, if (page) dma_free_contiguous(dev, page, size); + + return; + +fail: + spin_unlock_irqrestore(&mapping->lock, flags); } static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c index a9159c012106..67ca5c20c521 100644 --- a/drivers/iommu/io-pgtable-fast.c +++ b/drivers/iommu/io-pgtable-fast.c @@ -127,7 +127,14 @@ #define PTE_SH_IDX(pte) (pte & AV8L_FAST_PTE_SH_MASK) -#define iopte_pmd_offset(pmds, base, iova) (pmds + ((iova - base) >> 12)) +#define iopte_pmd_offset(pmds, base, iova) \ +({ \ + typeof(iova) __iova = (iova); \ + typeof(base) __base = (base); \ + typeof(pmds) __pmds = (pmds); \ + (__iova < __base) ? ERR_PTR(-EINVAL) : \ + __pmds + ((__iova - ALIGN_DOWN(__base, SZ_2M)) >> AV8L_FAST_PAGE_SHIFT); \ +}) static inline dma_addr_t av8l_dma_addr(void *addr) { @@ -202,6 +209,12 @@ void av8l_fast_clear_stale_ptes(struct io_pgtable_ops *ops, u64 base, struct io_pgtable *iop = iof_pgtable_ops_to_pgtable(ops); av8l_fast_iopte *pmdp = iopte_pmd_offset(data->pmds, data->base, base); + if (IS_ERR(pmdp)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return; + } + for (i = base >> AV8L_FAST_PAGE_SHIFT; i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) { if (!(*pmdp & AV8L_FAST_PTE_VALID)) { @@ -254,6 +267,12 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova, unsigned long i, nptes = size >> AV8L_FAST_PAGE_SHIFT; av8l_fast_iopte pte; + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return -EINVAL; + } + pte = av8l_fast_prot_to_pte(data, prot); paddr &= AV8L_FAST_PTE_ADDR_MASK; for (i = 0; i < nptes; i++, paddr += SZ_4K) { @@ -286,6 +305,12 @@ __av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, ptep = iopte_pmd_offset(data->pmds, data->base, iova); nptes = size >> AV8L_FAST_PAGE_SHIFT; + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return 0; + } + memset(ptep, val, sizeof(*ptep) * nptes); av8l_clean_range(&iop->cfg, ptep, ptep + nptes); if (!allow_stale_tlb) @@ -295,10 +320,10 @@ __av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, } /* caller must take care of tlb cache maintenance */ -void av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, +size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { - __av8l_fast_unmap(ops, iova, size, true); + return __av8l_fast_unmap(ops, iova, size, true); } static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova, @@ -383,6 +408,12 @@ static bool av8l_fast_iova_coherent(struct io_pgtable_ops *ops, struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops); av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova); + if (IS_ERR(ptep)) { + pr_err("Invalid iova : 0x%lx, as it is less than base : 0x%llx\n", + iova, data->base); + return false; + } + return ((PTE_MAIR_IDX(*ptep) == AV8L_FAST_MAIR_ATTR_IDX_CACHE) && ((PTE_SH_IDX(*ptep) == AV8L_FAST_PTE_SH_OS) || (PTE_SH_IDX(*ptep) == AV8L_FAST_PTE_SH_IS))); diff --git a/drivers/media/platform/msm/cvp/cvp.c b/drivers/media/platform/msm/cvp/cvp.c index 87adb4fbd975..76f7444a43b8 100644 --- a/drivers/media/platform/msm/cvp/cvp.c +++ b/drivers/media/platform/msm/cvp/cvp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -135,6 +135,8 @@ static int msm_cvp_initialize_core(struct platform_device *pdev, INIT_LIST_HEAD(&core->instances); mutex_init(&core->lock); mutex_init(&core->clk_lock); + mutex_init(&core->idr_mtx); + idr_init(&core->sess_idr); core->state = CVP_CORE_UNINIT; for (i = SYS_MSG_INDEX(SYS_MSG_START); @@ -506,6 +508,8 @@ static int msm_cvp_remove(struct platform_device *pdev) msm_cvp_free_platform_resources(&core->resources); sysfs_remove_group(&pdev->dev.kobj, &msm_cvp_core_attr_group); dev_set_drvdata(&pdev->dev, NULL); + idr_destroy(&core->sess_idr); + mutex_destroy(&core->idr_mtx); mutex_destroy(&core->lock); mutex_destroy(&core->clk_lock); kfree(core); diff --git a/drivers/media/platform/msm/cvp/cvp_hfi.c b/drivers/media/platform/msm/cvp/cvp_hfi.c index 22340456d275..df430d2235f7 100644 --- a/drivers/media/platform/msm/cvp/cvp_hfi.c +++ b/drivers/media/platform/msm/cvp/cvp_hfi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -29,6 +30,7 @@ #include "cvp_hfi_helper.h" #include "cvp_hfi_io.h" #include "msm_cvp_dsp.h" +#include "msm_cvp.h" #define FIRMWARE_SIZE 0X00A00000 #define REG_ADDR_OFFSET_BITMASK 0x000FFFFF @@ -469,6 +471,7 @@ static int __dsp_suspend(struct iris_hfi_device *device, bool force, u32 flags) { int rc; struct cvp_hal_session *temp; + struct msm_cvp_inst *inst = NULL; if (msm_cvp_dsp_disable) return 0; @@ -480,9 +483,10 @@ static int __dsp_suspend(struct iris_hfi_device *device, bool force, u32 flags) /* don't suspend if cvp session is not paused */ if (!(temp->flags & SESSION_PAUSE)) { + inst = (struct msm_cvp_inst *)temp->session_id; dprintk(CVP_DSP, "%s: cvp session %x not paused\n", - __func__, hash32_ptr(temp)); + __func__, inst->sess_id); return -EBUSY; } } @@ -2224,12 +2228,17 @@ static void __session_clean(struct cvp_hal_session *session) { struct cvp_hal_session *temp, *next; struct iris_hfi_device *device; + struct msm_cvp_core *core = NULL; + struct msm_cvp_inst *inst = NULL; + void *tmp = NULL; if (!session || !session->device) { dprintk(CVP_WARN, "%s: invalid params\n", __func__); return; } device = session->device; + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + inst = (struct msm_cvp_inst *) session->session_id; dprintk(CVP_SESS, "deleted the session: %pK\n", session); /* * session might have been removed from the device list in @@ -2241,6 +2250,13 @@ static void __session_clean(struct cvp_hal_session *session) break; } } + /* Remove the IDR id assigned to this session */ + mutex_lock(&core->idr_mtx); + tmp = idr_remove(&core->sess_idr, inst->sess_id); + if (tmp != session) + dprintk(CVP_WARN, "%s: session\n", __func__); + mutex_unlock(&core->idr_mtx); + /* Poison the session handle with zeros */ *session = (struct cvp_hal_session){ {0} }; kfree(session); @@ -2278,6 +2294,9 @@ static int iris_hfi_session_init(void *device, void *session_id, struct cvp_hfi_cmd_sys_session_init_packet pkt; struct iris_hfi_device *dev; struct cvp_hal_session *s; + struct msm_cvp_core *core; + struct msm_cvp_inst *inst; + int id = 0; if (!device || !new_session) { dprintk(CVP_ERR, "%s - invalid input\n", __func__); @@ -2285,6 +2304,8 @@ static int iris_hfi_session_init(void *device, void *session_id, } dev = device; + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + inst = session_id; mutex_lock(&dev->lock); s = kzalloc(sizeof(*s), GFP_KERNEL); @@ -2295,15 +2316,35 @@ static int iris_hfi_session_init(void *device, void *session_id, s->session_id = session_id; s->device = dev; + + mutex_lock(&core->idr_mtx); + idr_preload(GFP_KERNEL); + + /* Need to think if we can use core->lock or dev->lock or need a + * different new lock for this? + */ + id = idr_alloc(&core->sess_idr, (void *)s, 0x7FFF0000, INT_MAX, GFP_NOWAIT); + idr_preload_end(); + mutex_unlock(&core->idr_mtx); + if (id < 0) { + dprintk(CVP_ERR, + "%s: idr allocation failed for session %pK of inst %pK\n", + __func__, s, session_id); + goto err_session_init_fail; + } + dprintk(CVP_SESS, - "%s: inst %pK, session %pK\n", __func__, session_id, s); + "%s: inst %pK, session %pK, idr_id = 0x%x\n", __func__, session_id, s, id); list_add_tail(&s->list, &dev->sess_head); __set_default_sys_properties(device); + inst->sess_id = id; + if (call_hfi_pkt_op(dev, session_init, &pkt, s)) { dprintk(CVP_ERR, "session_init: failed to create packet\n"); + inst->sess_id = 0x0000DEAD; goto err_session_init_fail; } @@ -2317,6 +2358,7 @@ static int iris_hfi_session_init(void *device, void *session_id, err_session_init_fail: if (s) __session_clean(s); + inst->sess_id = 0; *new_session = NULL; mutex_unlock(&dev->lock); return -EINVAL; @@ -2878,9 +2920,11 @@ static struct cvp_hal_session *__get_session(struct iris_hfi_device *device, u32 session_id) { struct cvp_hal_session *temp = NULL; + struct msm_cvp_inst *inst = NULL; list_for_each_entry(temp, &device->sess_head, list) { - if (session_id == hash32_ptr(temp)) + inst = (struct msm_cvp_inst *)temp->session_id; + if (session_id == inst->sess_id) return temp; } @@ -3059,6 +3103,7 @@ static int __response_handler(struct iris_hfi_device *device) /* Process the packet types that we're interested in */ process_system_msg(info, device, raw_packet); + /* This session_id is a double pointer to the idr_id of session */ session_id = get_session_id(info); /* * hfi_process_msg_packet provides a session_id that's a hashed @@ -3070,11 +3115,6 @@ static int __response_handler(struct iris_hfi_device *device) if (session_id) { struct cvp_hal_session *session = NULL; - if (upper_32_bits((uintptr_t)*session_id) != 0) { - dprintk(CVP_ERR, - "Upper 32-bits != 0 for sess_id=%pK\n", - *session_id); - } session = __get_session(device, (u32)(uintptr_t)*session_id); if (!session) { diff --git a/drivers/media/platform/msm/cvp/hfi_packetization.c b/drivers/media/platform/msm/cvp/hfi_packetization.c index 107e2d744fff..edb6caaf1d34 100644 --- a/drivers/media/platform/msm/cvp/hfi_packetization.c +++ b/drivers/media/platform/msm/cvp/hfi_packetization.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "hfi_packetization.h" @@ -208,7 +209,7 @@ inline int cvp_create_pkt_cmd_sys_session_init( pkt->size = sizeof(struct cvp_hfi_cmd_sys_session_init_packet); pkt->packet_type = HFI_CMD_SYS_SESSION_INIT; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->session_type = inst->prop.type; pkt->session_kmask = inst->prop.kernel_mask; pkt->session_prio = inst->prop.priority; @@ -266,13 +267,14 @@ int cvp_create_pkt_cmd_session_cmd(struct cvp_hal_session_cmd_pkt *pkt, int pkt_type, struct cvp_hal_session *session) { int rc = 0; + struct msm_cvp_inst *inst = session->session_id; if (!pkt) return -EINVAL; pkt->size = sizeof(struct cvp_hal_session_cmd_pkt); pkt->packet_type = pkt_type; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; return rc; } @@ -305,13 +307,14 @@ int cvp_create_pkt_cmd_session_set_buffers( { int rc = 0; struct cvp_hfi_cmd_session_set_buffers_packet *pkt; + struct msm_cvp_inst *inst = session->session_id; - if (!cmd || !session) + if (!cmd || !session || !inst) return -EINVAL; pkt = (struct cvp_hfi_cmd_session_set_buffers_packet *)cmd; pkt->packet_type = HFI_CMD_SESSION_CVP_SET_BUFFERS; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->buf_type.iova = iova; pkt->buf_type.size = size; pkt->size = sizeof(struct cvp_hfi_cmd_session_set_buffers_packet); @@ -324,13 +327,14 @@ int cvp_create_pkt_cmd_session_release_buffers( struct cvp_hal_session *session) { struct cvp_session_release_buffers_packet *pkt; + struct msm_cvp_inst *inst = session->session_id; - if (!cmd || !session) + if (!cmd || !session || !inst) return -EINVAL; pkt = (struct cvp_session_release_buffers_packet *)cmd; pkt->packet_type = HFI_CMD_SESSION_CVP_RELEASE_BUFFERS; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->num_buffers = 1; pkt->buffer_type = 0; pkt->size = sizeof(struct cvp_session_release_buffers_packet) + @@ -347,6 +351,7 @@ int cvp_create_pkt_cmd_session_send( int def_idx; struct cvp_hal_session_cmd_pkt *ptr = (struct cvp_hal_session_cmd_pkt *)in_pkt; + struct msm_cvp_inst *inst = session->session_id; if (!out_pkt || !in_pkt || !session) return -EINVAL; @@ -354,7 +359,7 @@ int cvp_create_pkt_cmd_session_send( if (ptr->size > MAX_HFI_PKT_SIZE * sizeof(unsigned int)) goto error_hfi_packet; - if (ptr->session_id != hash32_ptr(session)) + if (ptr->session_id != inst->sess_id) goto error_hfi_packet; def_idx = get_pkt_index(ptr); diff --git a/drivers/media/platform/msm/cvp/hfi_response_handler.c b/drivers/media/platform/msm/cvp/hfi_response_handler.c index 61a5aadbe34a..3e0d3b717dc1 100644 --- a/drivers/media/platform/msm/cvp/hfi_response_handler.c +++ b/drivers/media/platform/msm/cvp/hfi_response_handler.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -462,7 +462,7 @@ static struct msm_cvp_inst *cvp_get_inst_from_id(struct msm_cvp_core *core, retry: if (mutex_trylock(&core->lock)) { list_for_each_entry(inst, &core->instances, list) { - if (hash32_ptr(inst->session) == session_id) { + if (inst->sess_id == session_id) { match = true; break; } diff --git a/drivers/media/platform/msm/cvp/msm_cvp.c b/drivers/media/platform/msm/cvp/msm_cvp.c index fefc06d6aee2..346f21641519 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp.c +++ b/drivers/media/platform/msm/cvp/msm_cvp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp.h" @@ -14,6 +15,47 @@ struct cvp_power_level { unsigned long bw_sum; }; +void *get_sessObj_from_idr(struct msm_cvp_inst *inst) +{ + void *sessObj = NULL; + struct msm_cvp_core *core = NULL; + + if (!inst || !inst->core) { + dprintk(CVP_ERR, "%s: invalid params\n", __func__); + return NULL; + } + + core = inst->core; + mutex_lock(&core->idr_mtx); + sessObj = idr_find(&core->sess_idr, inst->sess_id); + mutex_unlock(&core->idr_mtx); + if (!sessObj) + dprintk(CVP_ERR, "%s: Could not find the sess obj for given idr id\n", + __func__); + + return sessObj; +} + +u32 get_sessId_from_idr(void *session) +{ + void *ptr = NULL; + u32 sess_id = -1; + struct msm_cvp_core *core = NULL; + + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + if (!session || !core) + return -EINVAL; + mutex_lock(&core->idr_mtx); + idr_for_each_entry(&core->sess_idr, ptr, sess_id) { + if (ptr == session) { + mutex_unlock(&core->idr_mtx); + return sess_id; + } + } + mutex_unlock(&core->idr_mtx); + return sess_id; +} + static int msm_cvp_get_session_info(struct msm_cvp_inst *inst, struct cvp_kmd_session_info *session) { @@ -30,7 +72,7 @@ static int msm_cvp_get_session_info(struct msm_cvp_inst *inst, return -ECONNRESET; s->cur_cmd_type = CVP_KMD_GET_SESSION_INFO; - session->session_id = hash32_ptr(inst->session); + session->session_id = inst->sess_id; dprintk(CVP_SESS, "%s: id 0x%x\n", __func__, session->session_id); s->cur_cmd_type = 0; @@ -699,6 +741,13 @@ static int msm_cvp_session_process_hfi_fence(struct msm_cvp_inst *inst, f->output_index = kfc->output_index; } + if (f->num_fences >= (MAX_HFI_FENCE_SIZE / 2)) { + dprintk(CVP_ERR, "%s: Max number of fences exceeded! Max number supported: %d", + __func__, (MAX_HFI_FENCE_SIZE / 2)); + cvp_free_fence_data(f); + msm_cvp_unmap_frame(inst, pkt->client_data.kdata); + goto exit; + } dprintk(CVP_SYNX, "%s: frameID %llu ktid %llu\n", __func__, f->frame_id, pkt->client_data.kdata); @@ -1227,7 +1276,7 @@ static int msm_cvp_session_stop(struct msm_cvp_inst *inst, sq->state = QUEUE_STOP; pr_info(CVP_DBG_TAG "Stop session: %pK session_id = %d\n", - "sess", inst, hash32_ptr(inst->session)); + "sess", inst, inst->sess_id); spin_unlock(&sq->lock); wake_up_all(&inst->session_queue.wq); @@ -1251,7 +1300,7 @@ int msm_cvp_session_queue_stop(struct msm_cvp_inst *inst) sq->state = QUEUE_STOP; dprintk(CVP_SESS, "Stop session queue: %pK session_id = %d\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); spin_unlock(&sq->lock); wake_up_all(&inst->session_queue.wq); @@ -1551,7 +1600,7 @@ static void cvp_clean_fence_queue(struct msm_cvp_inst *inst, int synx_state) ktid = f->pkt->client_data.kdata & (FENCE_BIT - 1); dprintk(CVP_SYNX, "%s: (%#x) flush frame %llu %llu wait_list\n", - __func__, hash32_ptr(inst->session), ktid, f->frame_id); + __func__, inst->sess_id, ktid, f->frame_id); list_del_init(&f->list); msm_cvp_unmap_frame(inst, f->pkt->client_data.kdata); @@ -1564,7 +1613,7 @@ static void cvp_clean_fence_queue(struct msm_cvp_inst *inst, int synx_state) ktid = f->pkt->client_data.kdata & (FENCE_BIT - 1); dprintk(CVP_SYNX, "%s: (%#x)flush frame %llu %llu sched_list\n", - __func__, hash32_ptr(inst->session), ktid, f->frame_id); + __func__, inst->sess_id, ktid, f->frame_id); cvp_cancel_synx(inst, CVP_INPUT_SYNX, f, synx_state); } @@ -1612,14 +1661,14 @@ static int cvp_flush_all(struct msm_cvp_inst *inst) return -ECONNRESET; dprintk(CVP_SESS, "session %llx (%#x)flush all starts\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); q = &inst->fence_cmd_queue; hdev = inst->core->device; cvp_clean_fence_queue(inst, SYNX_STATE_SIGNALED_CANCEL); dprintk(CVP_SESS, "%s: (%#x) send flush to fw\n", - __func__, hash32_ptr(inst->session)); + __func__, inst->sess_id); /* Send flush to FW */ rc = call_hfi_op(hdev, session_flush, (void *)inst->session); @@ -1636,7 +1685,7 @@ static int cvp_flush_all(struct msm_cvp_inst *inst) __func__, rc); dprintk(CVP_SESS, "%s: (%#x) received flush from fw\n", - __func__, hash32_ptr(inst->session)); + __func__, inst->sess_id); exit: rc = cvp_drain_fence_sched_list(inst); @@ -1859,10 +1908,10 @@ int msm_cvp_session_deinit(struct msm_cvp_inst *inst) return -EINVAL; } dprintk(CVP_SESS, "%s: inst %pK (%#x)\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); - session = (struct cvp_hal_session *)inst->session; - if (!session) + session = (struct cvp_hal_session *)get_sessObj_from_idr(inst); + if (!session || session != inst->session) return rc; rc = msm_cvp_comm_try_state(inst, MSM_CVP_CLOSE_DONE); @@ -1883,7 +1932,7 @@ int msm_cvp_session_init(struct msm_cvp_inst *inst) } dprintk(CVP_SESS, "%s: inst %pK (%#x)\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); /* set default frequency */ inst->clk_data.core_id = 0; diff --git a/drivers/media/platform/msm/cvp/msm_cvp.h b/drivers/media/platform/msm/cvp/msm_cvp.h index b21864b46f1c..b8ae6068de35 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp.h +++ b/drivers/media/platform/msm/cvp/msm_cvp.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _MSM_CVP_H_ @@ -34,4 +35,6 @@ int msm_cvp_session_init(struct msm_cvp_inst *inst); int msm_cvp_session_deinit(struct msm_cvp_inst *inst); int msm_cvp_session_queue_stop(struct msm_cvp_inst *inst); int cvp_stop_clean_fence_queue(struct msm_cvp_inst *inst); +void *get_sessObj_from_idr(struct msm_cvp_inst *inst); +u32 get_sessId_from_idr(void *session); #endif diff --git a/drivers/media/platform/msm/cvp/msm_cvp_buf.c b/drivers/media/platform/msm/cvp/msm_cvp_buf.c index 995c111edb7d..c16eed33efd3 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_buf.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_buf.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp_common.h" @@ -14,7 +14,7 @@ do { \ clear_bit(idx, &inst->dma_cache.usage_bitmap); \ dprintk(CVP_MEM, "clear %x bit %d dma_cache bitmap 0x%llx\n", \ - hash32_ptr(inst->session), smem->bitmap_index, \ + inst->sess_id, smem->bitmap_index, \ inst->dma_cache.usage_bitmap); \ } while (0) @@ -22,7 +22,7 @@ do { \ set_bit(idx, &inst->dma_cache.usage_bitmap); \ dprintk(CVP_MEM, "Set %x bit %d dma_cache bitmap 0x%llx\n", \ - hash32_ptr(inst->session), idx, \ + inst->sess_id, idx, \ inst->dma_cache.usage_bitmap); \ } while (0) @@ -36,7 +36,7 @@ void print_smem(u32 tag, const char *str, struct msm_cvp_inst *inst, if (smem->dma_buf) { dprintk(tag, "%s: %x : %s size %d flags %#x iova %#x idx %d ref %d", - str, hash32_ptr(inst->session), smem->dma_buf->name, + str, inst->sess_id, smem->dma_buf->name, smem->size, smem->flags, smem->device_addr, smem->bitmap_index, smem->refcount); } @@ -51,13 +51,13 @@ static void print_internal_buffer(u32 tag, const char *str, if (cbuf->smem->dma_buf) { dprintk(tag, "%s: %x : fd %d off %d %s size %d iova %#x", - str, hash32_ptr(inst->session), cbuf->fd, + str, inst->sess_id, cbuf->fd, cbuf->offset, cbuf->smem->dma_buf->name, cbuf->size, cbuf->smem->device_addr); } else { dprintk(tag, "%s: %x : idx %2d fd %d off %d size %d iova %#x", - str, hash32_ptr(inst->session), cbuf->fd, + str, inst->sess_id, cbuf->fd, cbuf->offset, cbuf->size, cbuf->smem->device_addr); } } @@ -77,7 +77,7 @@ void print_client_buffer(u32 tag, const char *str, dprintk(tag, "%s: %x : idx %2d fd %d off %d size %d type %d flags 0x%x\n", - str, hash32_ptr(inst->session), cbuf->index, cbuf->fd, + str, inst->sess_id, cbuf->index, cbuf->fd, cbuf->offset, cbuf->size, cbuf->type, cbuf->flags); } @@ -154,7 +154,7 @@ int msm_cvp_map_buf_dsp(struct msm_cvp_inst *inst, struct cvp_kmd_buffer *buf) } if (buf->index) { - rc = cvp_dsp_register_buffer(hash32_ptr(session), buf->fd, + rc = cvp_dsp_register_buffer(inst->sess_id, buf->fd, smem->dma_buf->size, buf->size, buf->offset, buf->index, (uint32_t)smem->device_addr); if (rc) { @@ -227,7 +227,7 @@ int msm_cvp_unmap_buf_dsp(struct msm_cvp_inst *inst, struct cvp_kmd_buffer *buf) } if (buf->index) { - rc = cvp_dsp_deregister_buffer(hash32_ptr(session), buf->fd, + rc = cvp_dsp_deregister_buffer(inst->sess_id, buf->fd, cbuf->smem->dma_buf->size, buf->size, buf->offset, buf->index, (uint32_t)cbuf->smem->device_addr); if (rc) { @@ -545,7 +545,7 @@ void msm_cvp_unmap_frame(struct msm_cvp_inst *inst, u64 ktid) ktid &= (FENCE_BIT - 1); dprintk(CVP_MEM, "%s: (%#x) unmap frame %llu\n", - __func__, hash32_ptr(inst->session), ktid); + __func__, inst->sess_id, ktid); found = false; mutex_lock(&inst->frames.lock); @@ -587,7 +587,7 @@ int msm_cvp_unmap_user_persist(struct msm_cvp_inst *inst, smem = pbuf->smem; dprintk(CVP_MEM, "unmap persist: %x %d %d %#x", - hash32_ptr(inst->session), pbuf->fd, + inst->sess_id, pbuf->fd, pbuf->size, smem->device_addr); if (smem->bitmap_index >= MAX_DMABUF_NUMS) { @@ -785,7 +785,7 @@ int msm_cvp_session_deinit_buffers(struct msm_cvp_inst *inst) list_for_each_entry_safe(cbuf, dummy, &inst->cvpdspbufs.list, list) { print_internal_buffer(CVP_MEM, "remove dspbufs", inst, cbuf); - rc = cvp_dsp_deregister_buffer(hash32_ptr(session), + rc = cvp_dsp_deregister_buffer(inst->sess_id, cbuf->fd, cbuf->smem->dma_buf->size, cbuf->size, cbuf->offset, cbuf->index, (uint32_t)cbuf->smem->device_addr); @@ -955,7 +955,7 @@ int cvp_release_arp_buffers(struct msm_cvp_inst *inst) if (buf->ownership == DRIVER) { dprintk(CVP_MEM, "%s: %x : fd %d %s size %d", - "free arp", hash32_ptr(inst->session), buf->fd, + "free arp", inst->sess_id, buf->fd, smem->dma_buf->name, buf->size); msm_cvp_smem_free(smem); kmem_cache_free(cvp_driver->smem_cache, smem); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_common.c b/drivers/media/platform/msm/cvp/msm_cvp_common.c index 69787e51cc3d..22c8aa9441ad 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_common.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_common.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -195,23 +196,31 @@ struct msm_cvp_inst *cvp_get_inst_validate(struct msm_cvp_core *core, { int rc = 0; struct cvp_hfi_device *hdev; - struct msm_cvp_inst *s; + struct msm_cvp_inst *inst; + void *sessObj = NULL; - s = cvp_get_inst(core, session_id); - if (!s) { - dprintk(CVP_ERR, "%s session doesn't exit\n", + inst = cvp_get_inst(core, session_id); + if (!inst) { + dprintk(CVP_ERR, "%s Inst doesn't exit\n", __builtin_return_address(0)); return NULL; } - hdev = s->core->device; - rc = call_hfi_op(hdev, validate_session, s->session, __func__); - if (rc) { - cvp_put_inst(s); - s = NULL; + sessObj = get_sessObj_from_idr(inst); + if (!sessObj || sessObj != inst->session) { + dprintk(CVP_ERR, + "Either sessionObj is null or not matching with inst->session\n"); + return NULL; } - return s; + hdev = inst->core->device; + rc = call_hfi_op(hdev, validate_session, sessObj, __func__); + if (rc) { + cvp_put_inst(inst); + inst = NULL; + } + + return inst; } static void cvp_handle_session_cmd_done(enum hal_command_response cmd, @@ -486,7 +495,7 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data) } dprintk(CVP_SESS, "%s: cvp session %#x\n", __func__, - hash32_ptr(inst->session)); + inst->sess_id); signal_session_msg_receipt(cmd, inst); cvp_put_inst(inst); @@ -568,7 +577,7 @@ static void handle_session_error(enum hal_command_response cmd, void *data) hdev = inst->core->device; dprintk(CVP_ERR, "Session error received for inst %pK session %x\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); if (response->status == CVP_ERR_MAX_CLIENTS) { dprintk(CVP_WARN, "Too many clients, rejecting %pK", inst); @@ -901,7 +910,7 @@ static int msm_comm_session_abort(struct msm_cvp_inst *inst) abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE); dprintk(CVP_WARN, "%s: inst %pK session %x\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); rc = call_hfi_op(hdev, session_abort, (void *)inst->session); if (rc) { dprintk(CVP_ERR, @@ -914,7 +923,7 @@ static int msm_comm_session_abort(struct msm_cvp_inst *inst) inst->core->resources.msm_cvp_hw_rsp_timeout)); if (!rc) { dprintk(CVP_ERR, "%s: inst %pK session %x abort timed out\n", - __func__, inst, hash32_ptr(inst->session)); + __func__, inst, inst->sess_id); call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); dump_hfi_queue(hdev->hfi_device_data); msm_cvp_comm_generate_sys_error(inst); @@ -1268,7 +1277,7 @@ int msm_cvp_comm_try_state(struct msm_cvp_inst *inst, int state) } dprintk(CVP_SESS, "Trying to move inst: %pK (%#x) from: %#x to %#x\n", - inst, hash32_ptr(inst->session), inst->state, state); + inst, inst->sess_id, inst->state, state); mutex_lock(&inst->sync_lock); if (inst->state == MSM_CVP_CORE_INVALID) { @@ -1281,7 +1290,7 @@ int msm_cvp_comm_try_state(struct msm_cvp_inst *inst, int state) flipped_state = get_flipped_state(inst->state, state); dprintk(CVP_SESS, "inst: %pK (%#x) flipped_state = %#x %x\n", - inst, hash32_ptr(inst->session), flipped_state, state); + inst, inst->sess_id, flipped_state, state); switch (flipped_state) { case MSM_CVP_CORE_UNINIT_DONE: case MSM_CVP_CORE_INIT: @@ -1491,7 +1500,7 @@ int msm_cvp_comm_kill_session(struct msm_cvp_inst *inst) return 0; } dprintk(CVP_WARN, "%s: inst %pK, session %x state %d\n", __func__, - inst, hash32_ptr(inst->session), inst->state); + inst, inst->sess_id, inst->state); /* * We're internally forcibly killing the session, if fw is aware of * the session send session_abort to firmware to clean up and release @@ -1503,7 +1512,7 @@ int msm_cvp_comm_kill_session(struct msm_cvp_inst *inst) if (rc) { dprintk(CVP_ERR, "%s: inst %pK session %x abort failed\n", - __func__, inst, hash32_ptr(inst->session)); + __func__, inst, inst->sess_id); change_cvp_inst_state(inst, MSM_CVP_CORE_INVALID); } else { change_cvp_inst_state(inst, MSM_CVP_CORE_UNINIT); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_core.c b/drivers/media/platform/msm/cvp/msm_cvp_core.c index 5347eade1782..b98d43730988 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_core.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_core.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -165,7 +166,7 @@ void *msm_cvp_open(int core_id, int session_type) list_for_each_entry(inst, &core->instances, list) dprintk(CVP_ERR, "inst %pK, cmd %d id %d\n", inst, inst->cur_cmd_type, - hash32_ptr(inst->session)); + inst->sess_id); mutex_unlock(&core->lock); return NULL; @@ -369,7 +370,7 @@ int msm_cvp_destroy(struct msm_cvp_inst *inst) synx_uninitialize(inst->synx_session_id); pr_info(CVP_DBG_TAG "Closed cvp instance: %pK session_id = %d\n", - "sess", inst, hash32_ptr(inst->session)); + "sess", inst, inst->sess_id); if (inst->cur_cmd_type) dprintk(CVP_ERR, "deleted instance has pending cmd %d\n", inst->cur_cmd_type); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_internal.h b/drivers/media/platform/msm/cvp/msm_cvp_internal.h index 533e16cfce56..31495ad77d35 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_internal.h +++ b/drivers/media/platform/msm/cvp/msm_cvp_internal.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _MSM_CVP_INTERNAL_H_ @@ -290,6 +291,8 @@ struct msm_cvp_core { unsigned long curr_freq; struct cvp_cycle_info dyn_clk; atomic64_t kernel_trans_id; + struct idr sess_idr; + struct mutex idr_mtx; }; struct msm_cvp_inst { @@ -301,6 +304,7 @@ struct msm_cvp_inst { struct cvp_session_queue session_queue_fence; struct cvp_session_event event_handler; void *session; + u32 sess_id; enum instance_state state; struct msm_cvp_list freqs; struct msm_cvp_list persistbufs; diff --git a/drivers/media/platform/msm/cvp/msm_cvp_synx.c b/drivers/media/platform/msm/cvp/msm_cvp_synx.c index f70fb4013058..4580c582174b 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_synx.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_synx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp_common.h" @@ -20,7 +21,7 @@ void cvp_dump_fence_queue(struct msm_cvp_inst *inst) ssid = inst->synx_session_id; mutex_lock(&q->lock); dprintk(CVP_WARN, "inst %x fence q mode %d, ssid %d\n", - hash32_ptr(inst->session), q->mode, ssid.client_id); + inst->sess_id, q->mode, ssid.client_id); dprintk(CVP_WARN, "fence cmdq wait list:\n"); list_for_each_entry(f, &q->wait_list, list) { diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 5694d18b43d5..990c53398b9b 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -27,8 +27,10 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_colour_space *colour_info; struct hfi_buffer_requirements *bufreq; struct hfi_extradata_input_crop *crop; + struct hfi_dpb_counts *dpb_count; + u32 ptype, rem_bytes; + u32 size_read = 0; u8 *data_ptr; - u32 ptype; inst->error = HFI_ERR_NONE; @@ -38,80 +40,120 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, break; default: inst->error = HFI_ERR_SESSION_INVALID_PARAMETER; - goto done; + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); + return; } event.event_type = pkt->event_data1; num_properties_changed = pkt->event_data2; - if (!num_properties_changed) { - inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; - goto done; - } + if (!num_properties_changed) + goto error; data_ptr = (u8 *)&pkt->ext_event_data[0]; + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt); + do { + if (rem_bytes < sizeof(u32)) + goto error; ptype = *((u32 *)data_ptr); + + data_ptr += sizeof(u32); + rem_bytes -= sizeof(u32); + switch (ptype) { case HFI_PROPERTY_PARAM_FRAME_SIZE: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_framesize)) + goto error; + frame_sz = (struct hfi_framesize *)data_ptr; event.width = frame_sz->width; event.height = frame_sz->height; - data_ptr += sizeof(*frame_sz); + size_read = sizeof(struct hfi_framesize); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_profile_level)) + goto error; + profile_level = (struct hfi_profile_level *)data_ptr; event.profile = profile_level->profile; event.level = profile_level->level; - data_ptr += sizeof(*profile_level); + size_read = sizeof(struct hfi_profile_level); break; case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_bit_depth)) + goto error; + pixel_depth = (struct hfi_bit_depth *)data_ptr; event.bit_depth = pixel_depth->bit_depth; - data_ptr += sizeof(*pixel_depth); + size_read = sizeof(struct hfi_bit_depth); break; case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_pic_struct)) + goto error; + pic_struct = (struct hfi_pic_struct *)data_ptr; event.pic_struct = pic_struct->progressive_only; - data_ptr += sizeof(*pic_struct); + size_read = sizeof(struct hfi_pic_struct); break; case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_colour_space)) + goto error; + colour_info = (struct hfi_colour_space *)data_ptr; event.colour_space = colour_info->colour_space; - data_ptr += sizeof(*colour_info); + size_read = sizeof(struct hfi_colour_space); break; case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(u32)) + goto error; + event.entropy_mode = *(u32 *)data_ptr; - data_ptr += sizeof(u32); + size_read = sizeof(u32); break; case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_buffer_requirements)) + goto error; + bufreq = (struct hfi_buffer_requirements *)data_ptr; event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver); data_ptr += sizeof(*bufreq); + event.buf_count = hfi_bufreq_get_count_min(bufreq, ver); + size_read = sizeof(struct hfi_buffer_requirements); break; case HFI_INDEX_EXTRADATA_INPUT_CROP: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_extradata_input_crop)) + goto error; + crop = (struct hfi_extradata_input_crop *)data_ptr; event.input_crop.left = crop->left; event.input_crop.top = crop->top; event.input_crop.width = crop->width; event.input_crop.height = crop->height; - data_ptr += sizeof(*crop); + size_read = sizeof(struct hfi_extradata_input_crop); + break; + case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS: + if (rem_bytes < sizeof(struct hfi_dpb_counts)) + goto error; + + dpb_count = (struct hfi_dpb_counts *)data_ptr; + event.buf_count = dpb_count->fw_min_cnt; + size_read = sizeof(struct hfi_dpb_counts); break; default: + size_read = 0; break; } + data_ptr += size_read; + rem_bytes -= size_read; num_properties_changed--; } while (num_properties_changed > 0); -done: + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); + return; + +error: + inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); } diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 3cf9ba563cfc..198d22d73724 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -379,9 +379,21 @@ static int olaps_cmp(const void *a, const void *b) return st == 0 ? ed : st; } +/** + * fastrpc_get_buff_overlaps - Detect and handle buffer overlaps in RPC args + * @ctx: The invoke context containing buffer information + * + * This function detects overlapping memory regions in the RPC arguments and + * adjusts the memory mapping accordingly. It handles ION and non-ION buffers + * separately to prevent incorrect overlap detection between different buf types. + * For each buffer type: + * - If a buffer overlaps with a previous buffer of the same type, it adjusts + * the mapping to avoid the overlap + * - If no overlap is detected, it uses the full buffer range + */ static void fastrpc_get_buff_overlaps(struct fastrpc_invoke_ctx *ctx) { - u64 max_end = 0; + u64 ion_buf_end_pos = 0, non_ion_buf_end_pos = 0; int i; for (i = 0; i < ctx->nbufs; ++i) { @@ -393,24 +405,29 @@ static void fastrpc_get_buff_overlaps(struct fastrpc_invoke_ctx *ctx) sort(ctx->olaps, ctx->nbufs, sizeof(*ctx->olaps), olaps_cmp, NULL); for (i = 0; i < ctx->nbufs; ++i) { - /* Falling inside previous range */ - if (ctx->olaps[i].start < max_end) { - ctx->olaps[i].mstart = max_end; - ctx->olaps[i].mend = ctx->olaps[i].end; - ctx->olaps[i].offset = max_end - ctx->olaps[i].start; + /* Separate ION and non-ION buffers; fd <= 0 indicates non-ION */ + u64 *last_buf_end = (ctx->args[ctx->olaps[i].raix].fd <= 0) ? + &non_ion_buf_end_pos : &ion_buf_end_pos; - if (ctx->olaps[i].end > max_end) { - max_end = ctx->olaps[i].end; + if (ctx->olaps[i].start < *last_buf_end) { + /* Overlap detected within same buffer type */ + ctx->olaps[i].mstart = *last_buf_end; + ctx->olaps[i].mend = ctx->olaps[i].end; + ctx->olaps[i].offset = *last_buf_end - ctx->olaps[i].start; + + if (ctx->olaps[i].end > *last_buf_end) { + *last_buf_end = ctx->olaps[i].end; } else { ctx->olaps[i].mend = 0; ctx->olaps[i].mstart = 0; } } else { + /* No overlap, assign full range */ ctx->olaps[i].mend = ctx->olaps[i].end; ctx->olaps[i].mstart = ctx->olaps[i].start; ctx->olaps[i].offset = 0; - max_end = ctx->olaps[i].end; + *last_buf_end = ctx->olaps[i].end; } } } @@ -813,6 +830,22 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) PAGE_SHIFT; pages[i].size = (pg_end - pg_start + 1) * PAGE_SIZE; + /* + * Check for page range overflow and validate page + * range is not greater than map buffer range. + * This prevents potential buffer overflow + * and memory corruption that could be exploited. + */ + if (pages[i].addr > (ULLONG_MAX - pages[i].size) || + (pages[i].addr + pages[i].size) > + (ctx->maps[i]->phys + ctx->maps[i]->size)) { + err = -EFAULT; + dev_err(dev, + "Invalid buffer addr 0x%llx len 0x%llx IPA 0x%llx size 0x%llx fd %d\n", + ctx->args[i].ptr, len, ctx->maps[i]->phys, + ctx->maps[i]->size, ctx->maps[i]->fd); + goto bail; + } } else { if (ctx->olaps[oix].offset == 0) { diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 329e7ebbd191..eb45e4908968 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2007 Google, Inc. * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_qpic_nand.h" @@ -1944,7 +1944,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > 4) { + if (num_zero_bits > info->flash_dev.ecc_capability) { *erased_page = false; goto free_mem; } @@ -1955,8 +1955,8 @@ free_dma: num_zero_bits = last_pos = next_pos = 0; ecc_temp += chip->ecc_parity_bytes; } - - if ((n == cwperpage) && (num_zero_bits <= 4)) + if ((n == cwperpage) && + (num_zero_bits <= info->flash_dev.ecc_capability)) *erased_page = true; free_mem: kfree(ecc); @@ -2163,6 +2163,33 @@ static int msm_nand_read_pagescope(struct mtd_info *mtd, loff_t from, goto free_dma; /* Check for flash status errors */ pageerr = rawerr = 0; + + /* + * PAGE_ERASED bit will set only if all + * CODEWORD_ERASED bit of all codewords + * of the page is set. + * + * PAGE_ERASED bit is a 'logical and' of all + * CODEWORD_ERASED bit of all codewords i.e. + * even if one codeword is detected as not + * an erased codeword, PAGE_ERASED bit will unset. + */ + for (n = rw_params.start_sector; n < cwperpage; n++) { + if ((dma_buffer->result[n].erased_cw_status & + (1 << PAGE_ERASED)) && + (dma_buffer->result[n].buffer_status & + NUM_ERRORS)) { + err = msm_nand_is_erased_page_ps(mtd, + from, ops, + &rw_params, + &erased_page); + if (err) + goto free_dma; + if (erased_page) + rawerr = -EIO; + break; + } + } for (n = rw_params.start_sector; n < cwperpage; n++) { if (dma_buffer->result[n].flash_status & (FS_OP_ERR | FS_MPU_ERR)) { @@ -2554,7 +2581,7 @@ free_dma: if (last_pos < ecc_bytes_percw_in_bits) num_zero_bits++; - if (num_zero_bits > 4) { + if (num_zero_bits > info->flash_dev.ecc_capability) { *erased_page = false; goto free_mem; } @@ -2566,7 +2593,8 @@ free_dma: ecc_temp += chip->ecc_parity_bytes; } - if ((n == cwperpage) && (num_zero_bits <= 4)) + if ((n == cwperpage) && + (num_zero_bits <= info->flash_dev.ecc_capability)) *erased_page = true; free_mem: kfree(ecc); @@ -2760,6 +2788,33 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, goto free_dma; /* Check for flash status errors */ pageerr = rawerr = 0; + + /* + * PAGE_ERASED bit will set only if all + * CODEWORD_ERASED bit of all codewords + * of the page is set. + * + * PAGE_ERASED bit is a 'logical and' of all + * CODEWORD_ERASED bit of all codewords i.e. + * even if one codeword is detected as not + * an erased codeword, PAGE_ERASED bit will unset. + */ + for (n = rw_params.start_sector; n < cwperpage; n++) { + if ((dma_buffer->result[n].erased_cw_status & + (1 << PAGE_ERASED)) && + (dma_buffer->result[n].buffer_status & + NUM_ERRORS)) { + err = msm_nand_is_erased_page(mtd, + from, ops, + &rw_params, + &erased_page); + if (err) + goto free_dma; + if (erased_page) + rawerr = -EIO; + break; + } + } for (n = rw_params.start_sector; n < cwperpage; n++) { if (dma_buffer->result[n].flash_status & (FS_OP_ERR | FS_MPU_ERR)) { diff --git a/drivers/mtd/devices/msm_qpic_nand.h b/drivers/mtd/devices/msm_qpic_nand.h index cc1df16f5b3d..0297ad9697c4 100644 --- a/drivers/mtd/devices/msm_qpic_nand.h +++ b/drivers/mtd/devices/msm_qpic_nand.h @@ -154,7 +154,10 @@ #define RESET_ERASED_DET (1 << AUTO_DETECT_RES) #define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) #define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) -#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) +#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC | SET_N_MAX_ZEROS) +#define N_MAX_ZEROS 2 +#define MAX_ECC_BIT_FLIPS 4 +#define SET_N_MAX_ZEROS (MAX_ECC_BIT_FLIPS << N_MAX_ZEROS) #define MSM_NAND_ERASED_CW_DETECT_STATUS(info) MSM_NAND_REG(info, 0x300EC) #define PAGE_ALL_ERASED 7 @@ -163,6 +166,7 @@ #define CODEWORD_ERASED 4 #define ERASED_PAGE ((1 << PAGE_ALL_ERASED) | (1 << PAGE_ERASED)) #define ERASED_CW ((1 << CODEWORD_ALL_ERASED) | (1 << CODEWORD_ERASED)) +#define NUM_ERRORS 0x1f #define MSM_NAND_CTRL(info) MSM_NAND_REG(info, 0x30F00) #define BAM_MODE_EN 0 diff --git a/drivers/net/wireless/cnss/Makefile b/drivers/net/wireless/cnss/Makefile index c1ca4c3821e6..567ef214d7dd 100644 --- a/drivers/net/wireless/cnss/Makefile +++ b/drivers/net/wireless/cnss/Makefile @@ -3,7 +3,9 @@ # Makefile for CNSS platform driver # -obj-$(CONFIG_CNSS_PCI) += cnss_pci.o -obj-$(CONFIG_CNSS_SDIO) += cnss_sdio.o -obj-$(CONFIG_CNSS) += cnss_common.o -obj-$(CONFIG_CNSS_LOGGER) += logger/ +obj-$(CONFIG_CNSS) += cnss.o + +cnss-$(CONFIG_CNSS_PCI) += cnss_pci.o +cnss-$(CONFIG_CNSS_SDIO) += cnss_sdio.o +cnss-y += cnss_common.o +obj-$(CONFIG_CNSS_LOGGER) += logger/ diff --git a/drivers/net/wireless/cnss/cnss_common.c b/drivers/net/wireless/cnss/cnss_common.c index dbdd13e0abca..534f81f320a6 100644 --- a/drivers/net/wireless/cnss/cnss_common.c +++ b/drivers/net/wireless/cnss/cnss_common.c @@ -242,13 +242,21 @@ int cnss_set_cpus_allowed_ptr(struct task_struct *task, ulong cpu) } EXPORT_SYMBOL(cnss_set_cpus_allowed_ptr); -/* wlan prop driver cannot invoke show_stack - * function directly, so to invoke this function it - * call wcnss_dump_stack function - */ +#define ENTRIES_COUNT 32 void cnss_dump_stack(struct task_struct *task) { - show_stack(task, NULL); + const int cnss_spaces = 4; + unsigned long cnss_entries[ENTRIES_COUNT] = {0}; + struct stack_trace cnss_trace = { + .nr_entries = 0, + .skip = 0, + .entries = &cnss_entries[0], + .max_entries = ENTRIES_COUNT, + }; + + save_stack_trace_tsk(task, &cnss_trace); + stack_trace_print(cnss_entries, cnss_trace.nr_entries, + cnss_spaces); } EXPORT_SYMBOL(cnss_dump_stack); diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 9d588b5ed6d8..623149274fa3 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -1618,6 +1620,43 @@ static void cnss_pcie_reset_platform_ops(struct device *dev) dev->platform_data = NULL; } +#if IS_ENABLED(CONFIG_ARCH_QCOM) +/** + * cnss_pci_of_reserved_mem_device_init() - Assign reserved memory region + * to given PCI device + * @pdev: context pointer of pdev + * + * This function shall call corresponding of_reserved_mem_device* API to + * assign reserved memory region to PCI device based on where the memory is + * defined and attached to (platform device of_node or PCI device of_node) + * in device tree. + * + * Return: 0 for success, negative value for error + */ +static int cnss_pci_of_reserved_mem_device_init(struct pci_dev *pdev) +{ + struct device *dev_pci = &pdev->dev; + int ret; + + /* Use of_reserved_mem_device_init_by_idx() if reserved memory is + * attached to platform device of_node. + */ + ret = of_reserved_mem_device_init(dev_pci); + if (ret) + pr_err("Failed to init reserved mem device, err = %d\n", + ret); + if (dev_pci->cma_area) + pr_debug("CMA area is %s\n", cma_get_name(dev_pci->cma_area)); + + return ret; +} +#else +static int cnss_pci_of_reserved_mem_device_init(struct pci_dev *pdev) +{ + return 0; +} +#endif + static int cnss_wlan_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1634,6 +1673,7 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, atomic_set(&penv->fw_available, 0); penv->device_id = pdev->device; + cnss_pci_of_reserved_mem_device_init(pdev); if (penv->smmu_iova_len) { ret = cnss_smmu_init(&pdev->dev); if (ret) { diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index c8e23ac53d2f..cef10b457073 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -2179,6 +2179,11 @@ int ep_pcie_core_disable_endpoint(void) if (atomic_read(&dev->host_wake_pending)) { EP_PCIE_DBG(dev, "PCIe V%d: wake pending, init wakeup\n", dev->rev); + /* + * Clear the wake pending otherwise ep_pcie_core_wakeup_host_internal + * will return without WAKE toggle + */ + atomic_set(&dev->host_wake_pending, 0); ep_pcie_core_wakeup_host_internal(EP_PCIE_EVENT_PM_D3_COLD); } diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index d7d9ecad8d73..141ce1157e9c 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -39,7 +39,7 @@ /* Wait time on the device for Host to set BHI_INTVEC */ #define MHI_BHI_INTVEC_MAX_CNT 200 #define MHI_BHI_INTVEC_WAIT_MS 50 -#define MHI_WAKEUP_TIMEOUT_CNT 20 +#define MHI_WAKEUP_TIMEOUT_CNT 25 #define MHI_MASK_CH_EV_LEN 32 #define MHI_RING_CMD_ID 0 #define MHI_RING_PRIMARY_EVT_ID 1 diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index 89190bfeadfc..e8d9300c7812 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -12,6 +12,7 @@ #include "mhi_hwio.h" #include "mhi_sm.h" #include +#include #define MHI_SM_DBG(fmt, args...) \ mhi_log(MHI_MSG_DBG, fmt, ##args) @@ -28,7 +29,11 @@ #define PCIE_EP_TIMER_US 500000000 #define MHI_IPA_DISABLE_DELAY_MS 10 #define MHI_IPA_DISABLE_COUNTER 20 +/* Maximum wait time for D state transitions to D3hot */ +#define M3_DO_WAKEUP_TIMEOUT_MS 2500 +static void wait_d3_and_wakeup(struct work_struct *work); +static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate); static inline const char *mhi_sm_dev_event_str(enum mhi_dev_event state) { @@ -235,6 +240,8 @@ struct mhi_sm_dev { struct mutex mhi_state_lock; bool syserr_occurred; struct workqueue_struct *mhi_sm_wq; + struct workqueue_struct *mhi_wake_wq; + struct work_struct mhi_wake_work; atomic_t pending_device_events; atomic_t pending_pcie_events; struct mhi_sm_stats stats; @@ -734,12 +741,12 @@ exit: * mhi_sm_wakeup_host() - wakeup MHI-host *@event: MHI state chenge event * - * Sends wekup event to MHI-host via EP-PCIe, in case MHI is in M3 state. + * Sends wakeup event to MHI-host via EP-PCIe, in case MHI is in M3 state. * * Return: 0:success * negative: failure */ -static int mhi_sm_wakeup_host(enum mhi_dev_event event) +static int mhi_sm_wakeup_host(void) { int res = 0; enum ep_pcie_event pcie_event; @@ -754,7 +761,8 @@ static int mhi_sm_wakeup_host(enum mhi_dev_event event) } else if (mhi_sm_ctx->mhi_state == MHI_DEV_M3_STATE) { /* * Check and send D3_HOT to enable waking up the host - * using inband PME. + * using inband PME if the host is in D3_HOT state, otherwise + * send D3_COLD to wake up the host. */ if (mhi_sm_ctx->d_state == MHI_SM_EP_PCIE_D3_HOT_STATE) pcie_event = EP_PCIE_EVENT_PM_D3_HOT; @@ -906,9 +914,7 @@ static void mhi_sm_dev_event_manager(struct work_struct *work) break; case MHI_DEV_EVENT_HW_ACC_WAKEUP: case MHI_DEV_EVENT_CORE_WAKEUP: - res = mhi_sm_wakeup_host(chg_event->event); - if (res) - MHI_SM_ERR("Failed to wakeup MHI host\n"); + queue_work(mhi_sm_ctx->mhi_wake_wq, &mhi_sm_ctx->mhi_wake_work); break; case MHI_DEV_EVENT_CTRL_TRIG: case MHI_DEV_EVENT_M1_STATE: @@ -1119,9 +1125,19 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev) if (!mhi_sm_ctx->mhi_sm_wq) { MHI_SM_ERR("Failed to create singlethread_workqueue: sm_wq\n"); res = -ENOMEM; - goto fail_init_wq; + goto fail_init_sm_wq; } + if (!mhi_sm_ctx->mhi_wake_wq) + mhi_sm_ctx->mhi_wake_wq = alloc_workqueue( + "mhi_wake_wq", WQ_HIGHPRI | WQ_UNBOUND, 1); + if (!mhi_sm_ctx->mhi_wake_wq) { + MHI_SM_ERR("Failed to create singlethread_workqueue: wake_wq\n"); + res = -ENOMEM; + goto fail_init_wake_wq; + } + INIT_WORK(&mhi_sm_ctx->mhi_wake_work, wait_d3_and_wakeup); + mutex_init(&mhi_sm_ctx->mhi_state_lock); mhi_sm_ctx->mhi_dev = mhi_dev; mhi_sm_ctx->mhi_state = MHI_DEV_RESET_STATE; @@ -1134,7 +1150,10 @@ int mhi_dev_sm_init(struct mhi_dev *mhi_dev) MHI_SM_FUNC_EXIT(); return 0; -fail_init_wq: +fail_init_wake_wq: + flush_workqueue(mhi_sm_ctx->mhi_sm_wq); + destroy_workqueue(mhi_sm_ctx->mhi_sm_wq); +fail_init_sm_wq: mhi_sm_ctx = NULL; mhi_sm_debugfs_destroy(); return res; @@ -1162,20 +1181,20 @@ int mhi_dev_sm_exit(struct mhi_dev *mhi_dev) EXPORT_SYMBOL(mhi_dev_sm_exit); /** - * mhi_dev_sm_get_mhi_state() -Get current MHI state. + * mhi_dev_sm_get_mhi_pcie_states() -Get current MHI and Pcie states. * @state: return param * - * Returns the current MHI state of the state machine. + * Returns the current MHI and PCIe states of the state machine. * * Return: 0 success * -EINVAL: invalid param * -EFAULT: state machine isn't initialized */ -int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state) +static int mhi_dev_sm_get_mhi_pcie_states(uint32_t *mstate, uint32_t *dstate) { MHI_SM_FUNC_ENTRY(); - if (!state) { + if (!mstate || !dstate) { MHI_SM_ERR("Fail: Null argument\n"); return -EINVAL; } @@ -1183,15 +1202,60 @@ int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state) MHI_SM_ERR("Fail: MHI SM is not initialized\n"); return -EFAULT; } - *state = mhi_sm_ctx->mhi_state; + mutex_lock(&mhi_sm_ctx->mhi_state_lock); + *mstate = mhi_sm_ctx->mhi_state; + *dstate = mhi_sm_ctx->d_state; + mutex_unlock(&mhi_sm_ctx->mhi_state_lock); MHI_SM_DBG("state machine states are: %s and %s\n", - mhi_sm_mstate_str(*state), - mhi_sm_dstate_str(mhi_sm_ctx->d_state)); + mhi_sm_mstate_str(*mstate), + mhi_sm_dstate_str(*dstate)); MHI_SM_FUNC_EXIT(); return 0; } -EXPORT_SYMBOL(mhi_dev_sm_get_mhi_state); + +static void wait_d3_and_wakeup(struct work_struct *work) +{ + struct mhi_sm_dev *mhi_sm_ctx = container_of(work, struct mhi_sm_dev, mhi_wake_work); + enum mhi_dev_state mstate; + enum mhi_sm_ep_pcie_state dstate; + ktime_t timeout = 0; + + if (mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate)) { + MHI_SM_ERR("Unable to read states\n"); + return; + } + /* + * Handle host wakeup in M3 + D0 states. + * When a MHI WAKE request is received while device is in D0, + * wait for D3 and wakeup the host using inband PME. + * If the MHI state changes to M0 while waiting for D3, + * exit, since both MHI and the device are in active state + */ + if (dstate == MHI_SM_EP_PCIE_D0_STATE) { + timeout = ktime_add_ms(ktime_get(), M3_DO_WAKEUP_TIMEOUT_MS); + while (1) { + mhi_dev_sm_get_mhi_pcie_states(&mstate, &dstate); + if (mstate == MHI_DEV_M0_STATE) { + MHI_SM_DBG("M0 state received\n"); + return; + } + if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE || + dstate == MHI_SM_EP_PCIE_D3_COLD_STATE) { + MHI_SM_DBG("D3 state received\n"); + goto send_host_wakeup; + } + if (ktime_after(ktime_get(), timeout)) { + MHI_SM_ERR("Neither received D3 nor M0 in stipulated time\n"); + return; + } + usleep_range(1000, 2000); + } + } +send_host_wakeup: + if (dstate == MHI_SM_EP_PCIE_D3_HOT_STATE || dstate == MHI_SM_EP_PCIE_D3_COLD_STATE) + mhi_sm_wakeup_host(); +} /** * mhi_dev_sm_set_ready() -Set MHI state to ready. diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.h b/drivers/platform/msm/mhi_dev/mhi_sm.h index 80ed0086472f..24e6daf46777 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.h +++ b/drivers/platform/msm/mhi_dev/mhi_sm.h @@ -42,7 +42,6 @@ int mhi_dev_sm_init(struct mhi_dev *dev); int mhi_dev_sm_exit(struct mhi_dev *dev); int mhi_dev_sm_set_ready(void); int mhi_dev_notify_sm_event(enum mhi_dev_event event); -int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state); int mhi_dev_sm_syserr(void); void mhi_dev_sm_pcie_handler(struct ep_pcie_notify *notify); diff --git a/drivers/staging/fw-api/fw/htc_services.h b/drivers/staging/fw-api/fw/htc_services.h index 50d57596a0b0..73eb52c32911 100644 --- a/drivers/staging/fw-api/fw/htc_services.h +++ b/drivers/staging/fw-api/fw/htc_services.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2012, 2014-2017, 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -93,6 +93,11 @@ typedef enum { */ #define HTT_DATA3_MSG_SVC MAKE_SERVICE_ID(HTT_SERVICE_GROUP,2) +/* HTT_DATA4_MSG_SVC + * H2T channel to transfer MSDU/MPDU queue info from host to target + */ +#define HTT_DATA4_MSG_SVC MAKE_SERVICE_ID(HTT_SERVICE_GROUP,3) + /* raw stream service (i.e. flash, tcmd, calibration apps) */ #define HTC_RAW_STREAMS_SVC MAKE_SERVICE_ID(HTC_TEST_GROUP,0) diff --git a/drivers/staging/fw-api/fw/htt.h b/drivers/staging/fw-api/fw/htt.h index e9d34066ff72..6e1874396d53 100644 --- a/drivers/staging/fw-api/fw/htt.h +++ b/drivers/staging/fw-api/fw/htt.h @@ -266,9 +266,14 @@ * 3.136 Add htt_ext_present flag in htt_tx_tcl_global_seq_metadata. * 3.137 Add more HTT_SDWF_MSDUQ_CFG_IND_ERROR codes. * 3.138 Add T2H MLO_LATENCY_REQ, H2T _RESP msg defs. + * 3.139 Add CLASS_INFO_IDX field in MLO_R_PEER_MAP msg. + * 3.140 Add H2T MPDUQ_AND_MSDUQ_INFO_HDR and MPDUQ_OF_MSDUQ_INFO defs. + * 3.141 Add H2T HTT_AST_INFO for RxOLE. + * 3.142 Add T2H GLOBAL_PEER_ID_UNMAP def, update H2T MPDUQ_OR_MSDUQ_INFO def. + * 3.143 Add T2H HAPS msg def. */ #define HTT_CURRENT_VERSION_MAJOR 3 -#define HTT_CURRENT_VERSION_MINOR 138 +#define HTT_CURRENT_VERSION_MINOR 143 #define HTT_NUM_TX_FRAG_DESC 1024 @@ -767,7 +772,10 @@ typedef enum { HTT_STATS_AST_ENTRY_TAG = 132, /* htt_ast_entry_tlv */ HTT_STATS_TX_PDEV_BE_DL_MU_OFDMA_STATS_TAG = 133, /* htt_tx_pdev_dl_be_mu_ofdma_sch_stats_tlv, TOPIC=advanced */ HTT_STATS_TX_PDEV_BE_UL_MU_OFDMA_STATS_TAG = 134, /* htt_tx_pdev_ul_be_mu_ofdma_sch_stats_tlv, TOPIC=advanced */ - HTT_STATS_TX_PDEV_RATE_STATS_BE_OFDMA_TAG = 135, /* htt_tx_pdev_rate_stats_be_ofdma_tlv */ + HTT_STATS_TX_PDEV_RATE_BE_BN_OFDMA_TAG = 135, /* htt_stats_tx_pdev_rate_be_bn_ofdma_tlv */ + /* retain deprecated name as an alias */ + HTT_STATS_TX_PDEV_RATE_STATS_BE_OFDMA_TAG = + HTT_STATS_TX_PDEV_RATE_BE_BN_OFDMA_TAG, HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_BE_STATS_TAG = 136, /* htt_rx_pdev_ul_mumimo_trig_be_stats_tlv, TOPIC=advanced */ HTT_STATS_TX_SELFGEN_BE_ERR_STATS_TAG = 137, /* htt_tx_selfgen_be_err_stats_tlv, TOPIC=advanced */ HTT_STATS_TX_SELFGEN_BE_STATS_TAG = 138, /* htt_tx_selfgen_be_stats_tlv, TOPIC=advanced */ @@ -775,7 +783,10 @@ typedef enum { HTT_STATS_TX_PDEV_BE_UL_MU_MIMO_STATS_TAG = 140, /* htt_tx_pdev_be_ul_mu_mimo_sch_stats_tlv */ HTT_STATS_RX_PDEV_BE_UL_MIMO_USER_STATS_TAG = 141, /* htt_rx_pdev_be_ul_mimo_user_stats_tlv */ HTT_STATS_RX_RING_STATS_TAG = 142, /* htt_rx_fw_ring_stats_tlv_v */ - HTT_STATS_RX_PDEV_BE_UL_TRIG_STATS_TAG = 143, /* htt_rx_pdev_be_ul_trigger_stats_tlv, TOPIC=advanced */ + HTT_STATS_RX_PDEV_BE_BN_UL_TRIG_TAG = 143, /* htt_stats_rx_pdev_be_bn_ul_trig_tlv, TOPIC=advanced */ + /* retain deprecated name as an alias */ + HTT_STATS_RX_PDEV_BE_UL_TRIG_STATS_TAG = + HTT_STATS_RX_PDEV_BE_BN_UL_TRIG_TAG, HTT_STATS_TX_PDEV_SAWF_RATE_STATS_TAG = 144, /* htt_tx_pdev_rate_stats_sawf_tlv, TOPIC=advanced */ HTT_STATS_STRM_GEN_MPDUS_TAG = 145, /* htt_stats_strm_gen_mpdus_tlv_t */ HTT_STATS_STRM_GEN_MPDUS_DETAILS_TAG = 146, /* htt_stats_strm_gen_mpdus_details_tlv_t */ @@ -844,6 +855,17 @@ typedef enum { HTT_STATS_PDEV_UL_MUMIMO_DENYLIST_STATS_TAG = 209, /* htt_stats_pdev_ulmumimo_denylist_stats_tlv */ HTT_STATS_PDEV_UL_MUMIMO_SEQ_TERM_STATS_TAG = 210, /* htt_stats_pdev_ulmumimo_seq_term_stats_tlv */ HTT_STATS_PDEV_UL_MUMIMO_HIST_INELIGIBILITY_TAG = 211, /* htt_stats_pdev_ulmumimo_hist_ineligibility_tlv */ + HTT_STATS_PHY_PAPRD_PB_TAG = 212, /* htt_stats_phy_paprd_pb_tlv */ + HTT_STATS_HDS_PROF_STATS_TAG = 213, /* htt_stat_hds_prof_stats_tlv */ + HTT_STATS_TX_PDEV_MDSB_NUM_USERS_HISTOGRAM_TLV_TAG = 214, /* htt_stats_tx_pdev_mdsb_num_users_histogram_tlv */ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_TAG = 215, /* htt_stats_tx_pdev_pending_seq_cnt_on_sched_post_hist_tlv */ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_TAG = 216, /* htt_stats_tx_pdev_pending_seq_cnt_in_hwq_hist_tlv */ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_TAG = 217, /* htt_stats_tx_pdev_pending_seq_cnt_in_txq_hist_tlv */ + HTT_STATS_SCHED_TXQ_EARLY_COMPL_TAG = 218, /* htt_stats_sched_txq_early_compl_tlv */ + HTT_STATS_RX_PDEV_BN_UL_OFDMA_USER_TAG = 219, /* htt_stats_rx_pdev_bn_ul_ofdma_user_tlv */ + HTT_STATS_TX_SELFGEN_BN_ERR_TAG = 220, /* htt_stats_tx_selfgen_bn_err_tlv, TOPIC=advanced */ + HTT_STATS_TX_SELFGEN_BN_TAG = 221, /* htt_stats_tx_selfgen_bn_tlv, TOPIC=advanced */ + HTT_STATS_TX_SELFGEN_BN_SCHED_STATUS_TAG = 222, /* htt_stats_tx_selfgen_bn_sched_status_tlv, TOPIC=advanced */ HTT_STATS_MAX_TAG, } htt_stats_tlv_tag_t; @@ -878,47 +900,50 @@ typedef htt_stats_tlv_tag_t htt_tlv_tag_t; /*=== host -> target messages ===============================================*/ enum htt_h2t_msg_type { - HTT_H2T_MSG_TYPE_VERSION_REQ = 0x0, - HTT_H2T_MSG_TYPE_TX_FRM = 0x1, - HTT_H2T_MSG_TYPE_RX_RING_CFG = 0x2, - HTT_H2T_MSG_TYPE_STATS_REQ = 0x3, - HTT_H2T_MSG_TYPE_SYNC = 0x4, - HTT_H2T_MSG_TYPE_AGGR_CFG = 0x5, - HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 0x6, - DEPRECATED_HTT_H2T_MSG_TYPE_MGMT_TX = 0x7, /* no longer used */ - HTT_H2T_MSG_TYPE_WDI_IPA_CFG = 0x8, - HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ = 0x9, - HTT_H2T_MSG_TYPE_AGGR_CFG_EX = 0xa, /* per vdev amsdu subfrm limit */ - HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, - HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, - HTT_H2T_MSG_TYPE_ADD_WDS_ENTRY = 0xd, - HTT_H2T_MSG_TYPE_DELETE_WDS_ENTRY = 0xe, - HTT_H2T_MSG_TYPE_RFS_CONFIG = 0xf, - HTT_H2T_MSG_TYPE_EXT_STATS_REQ = 0x10, - HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, - HTT_H2T_MSG_TYPE_RX_FSE_SETUP_CFG = 0x12, - HTT_H2T_MSG_TYPE_RX_FSE_OPERATION_CFG = 0x13, - HTT_H2T_MSG_TYPE_CHAN_CALDATA = 0x14, - HTT_H2T_MSG_TYPE_RX_FISA_CFG = 0x15, - HTT_H2T_MSG_TYPE_3_TUPLE_HASH_CFG = 0x16, - HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE = 0x17, - HTT_H2T_MSG_TYPE_HOST_PADDR_SIZE = 0x18, - HTT_H2T_MSG_TYPE_RXDMA_RXOLE_PPE_CFG = 0x19, - HTT_H2T_MSG_TYPE_VDEVS_TXRX_STATS_CFG = 0x1a, - HTT_H2T_MSG_TYPE_TX_MONITOR_CFG = 0x1b, - HTT_H2T_SAWF_DEF_QUEUES_MAP_REQ = 0x1c, - HTT_H2T_SAWF_DEF_QUEUES_UNMAP_REQ = 0x1d, - HTT_H2T_SAWF_DEF_QUEUES_MAP_REPORT_REQ = 0x1e, - HTT_H2T_MSG_TYPE_MSI_SETUP = 0x1f, - HTT_H2T_MSG_TYPE_STREAMING_STATS_REQ = 0x20, - HTT_H2T_MSG_TYPE_UMAC_HANG_RECOVERY_PREREQUISITE_SETUP = 0x21, + HTT_H2T_MSG_TYPE_VERSION_REQ = 0x0, + HTT_H2T_MSG_TYPE_TX_FRM = 0x1, + HTT_H2T_MSG_TYPE_RX_RING_CFG = 0x2, + HTT_H2T_MSG_TYPE_STATS_REQ = 0x3, + HTT_H2T_MSG_TYPE_SYNC = 0x4, + HTT_H2T_MSG_TYPE_AGGR_CFG = 0x5, + HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 0x6, + DEPRECATED_HTT_H2T_MSG_TYPE_MGMT_TX = 0x7, /* no longer used */ + HTT_H2T_MSG_TYPE_WDI_IPA_CFG = 0x8, + HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ = 0x9, + HTT_H2T_MSG_TYPE_AGGR_CFG_EX = 0xa, /* per vdev amsdu subfrm limit */ + HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_ADD_WDS_ENTRY = 0xd, + HTT_H2T_MSG_TYPE_DELETE_WDS_ENTRY = 0xe, + HTT_H2T_MSG_TYPE_RFS_CONFIG = 0xf, + HTT_H2T_MSG_TYPE_EXT_STATS_REQ = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, + HTT_H2T_MSG_TYPE_RX_FSE_SETUP_CFG = 0x12, + HTT_H2T_MSG_TYPE_RX_FSE_OPERATION_CFG = 0x13, + HTT_H2T_MSG_TYPE_CHAN_CALDATA = 0x14, + HTT_H2T_MSG_TYPE_RX_FISA_CFG = 0x15, + HTT_H2T_MSG_TYPE_3_TUPLE_HASH_CFG = 0x16, + HTT_H2T_MSG_TYPE_RX_FULL_MONITOR_MODE = 0x17, + HTT_H2T_MSG_TYPE_HOST_PADDR_SIZE = 0x18, + HTT_H2T_MSG_TYPE_RXDMA_RXOLE_PPE_CFG = 0x19, + HTT_H2T_MSG_TYPE_VDEVS_TXRX_STATS_CFG = 0x1a, + HTT_H2T_MSG_TYPE_TX_MONITOR_CFG = 0x1b, + HTT_H2T_SAWF_DEF_QUEUES_MAP_REQ = 0x1c, + HTT_H2T_SAWF_DEF_QUEUES_UNMAP_REQ = 0x1d, + HTT_H2T_SAWF_DEF_QUEUES_MAP_REPORT_REQ = 0x1e, + HTT_H2T_MSG_TYPE_MSI_SETUP = 0x1f, + HTT_H2T_MSG_TYPE_STREAMING_STATS_REQ = 0x20, + HTT_H2T_MSG_TYPE_UMAC_HANG_RECOVERY_PREREQUISITE_SETUP = 0x21, HTT_H2T_MSG_TYPE_UMAC_HANG_RECOVERY_SOC_START_PRE_RESET = 0x22, - HTT_H2T_MSG_TYPE_RX_CCE_SUPER_RULE_SETUP = 0x23, - HTT_H2T_MSG_TYPE_PRIMARY_LINK_PEER_MIGRATE_RESP = 0x24, - HTT_H2T_MSG_TYPE_TX_LATENCY_STATS_CFG = 0x25, - HTT_H2T_MSG_TYPE_TX_LCE_SUPER_RULE_SETUP = 0x26, - HTT_H2T_MSG_TYPE_SDWF_MSDUQ_RECFG_REQ = 0x27, - HTT_H2T_MSG_TYPE_MLO_LATENCY_STATS_RESP = 0x28, + HTT_H2T_MSG_TYPE_RX_CCE_SUPER_RULE_SETUP = 0x23, + HTT_H2T_MSG_TYPE_PRIMARY_LINK_PEER_MIGRATE_RESP = 0x24, + HTT_H2T_MSG_TYPE_TX_LATENCY_STATS_CFG = 0x25, + HTT_H2T_MSG_TYPE_TX_LCE_SUPER_RULE_SETUP = 0x26, + HTT_H2T_MSG_TYPE_SDWF_MSDUQ_RECFG_REQ = 0x27, + HTT_H2T_MSG_TYPE_MLO_LATENCY_STATS_RESP = 0x28, + HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR = 0x29, + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO = 0x2a, + HTT_H2T_MSG_TYPE_AST_INFO = 0x2b, /* keep this last */ HTT_H2T_NUM_MSGS @@ -11584,6 +11609,507 @@ PREPACK struct htt_h2t_mlo_latency_stats { ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MLO_LATENCY_STATS_NUM_OF_TX_PKT_S)); \ } while (0) +/** + * @brief host -> target msg to provide MSDUQ or MPDUQ for new TID in a peer + * + * MSG_TYPE => HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR + * + * @details + * struct htt_h2t_mpduq_and_msduq_info_hdr: + * HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR message is sent by the host to + * target to tell how many mpduq and msduq info TLVs, in units of bytes, + * are present in the htt payload + * Example message contents: + *------------------------------------------------------------------- + *| htt_h2t_mpduq_and_msduq_info_hdr | + *------------------------------------------------------------------- + *| mpduq_info_tlv -> tid 0 , peer_id 1 | + *------------------------------------------------------------------- + *| mpduq_info_tlv -> tid 6, peer_id 1 | + *------------------------------------------------------------------- + *| msduq_info_tlv -> tid 0, peer_id 1 | + *------------------------------------------------------------------- + *| msduq_info_tlv -> tid 0, peer_id 1 | + *------------------------------------------------------------------- + *| msduq_info_tlv -> tid 6, peer_id 1 | + *------------------------------------------------------------------- + * + * As shown in the above exampple, a single htt buffer can hold multiple mpduq + * and msduq info tlvs, the info within the tlvs will indicate the peer and tid + * to which they belong. + */ + +/* HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR */ +PREPACK struct htt_h2t_mpduq_and_msduq_info_hdr { + A_UINT32 msg_type: 8, /* bits 7:0 */ + payload_size_bytes: 12, /* bits 19:8 */ + reserved_1a: 12; /* bits 31:20 */ +} POSTPACK; + +#define HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_M 0x000FFF00 +#define HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_S 8 +#define HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_AND_MSDUQ_INFO_HDR_PAYLOAD_SIZE_BYTES_S)); \ + } while (0) + + +/** + * @brief host -> target message to provide mpduq for a tid in a peer + * + * MSG_TYPE => HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO + * + * @details + * struct htt_h2t_mpduq_or_msduq_info: + * HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO message is sent by the host to + * specify the configuration of new MPDUQ/MSDUQ for a tid in a peer. + * This message supports the following configuration information: + * 1. Info provided per MPDUQ: + * upper 32 bit address of 256 byte aligned physical address for mpduq + * mpduq number + * pn address space + * 2. Info provided per MSDUQ: + * upper 32 bit address of 256 byte aligned physical address for msduq + * msduq_number + * service class id + * + * The message is interpreted as follows for mpduq type: + * dword0 - b'7:0 - msg_type: Identifies msduq and mpduq info to FW + * This will be set to 0x2a + * (HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO) + * b'12:8 - msduq_mpduq_type: Indicate whether this TLV is for + * a MPDU queue or MSDU queue, and if for a MSDU queue, + * what type. + * For an MPDU queue, it will be fixed value of 30 based + * on enum HTT_H2T_TID_MSDUQ_MPDUQ_TYPE. + * b'16:13 - hw_link_id: Indicates which HW link the message is for. + * This HW link ID is mainly relevant for split PHY + * usecases to identify the correct link in same SOC. + * dword1 - b'31:0 - mpduq_address_39_8: 256 byte aligned mpduq physical + * address, since lowest octet is zero for 256 byte aligned + * physical addresses just passing upper 32 bits of + * 40 bit address + * dword2 - b'11:0 – peer_id + * b'16:12 – tid_num + * b'23:17 – reserved + * b'31:24 - pn_addr_32_39: Upper 8 bits of 40 bit pn physical address + * Note that the mpduq_number is formed from the combination of + * peer_id (in bits 11:0) + * tid_num (in bits 16:12) + * msduq_mpduq_type (in bits 21:17) + * dword3 - b'31:0 - pn_addr_0_31: Lower 32 bits of 40 bit pn physical address + * Additional reserved dwords for future use cases + * + * + * The message is interpreted as follows for any msduq type: + * dword0 - b'7:0 - msg_type: Identifies msduq info to fw + * This will be set to 0x2A + * (HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO) + * b'12:8 - msduq_mpduq_type: type of the msduq based on + * enum HTT_H2T_TID_MSDUQ_MPDUQ_TYPE + * b'16:13 - hw_link_id: Indicates which HW link the message is + * intended for. + * This HW link ID is mainly relevant for split PHY + * usecases to identify the correct link in same SOC. + * dword1 - b'11:0 – peer_id + * b'16:12 – tid_num + * b'23:17 – reserved + * b'31:24 - svc_class_id : service class id of the msduq + * Note that the tx_msduq_number is formed from the combination of + * peer_id (in bits 11:0) + * tid_num (in bits 16:12) + * msduq_mpduq_type (in bits 21:17) + * dword2 - b'31:0 - msduq_address_39_8: 256 byte aligned msduq physical + * address, since lowest octet is zero for 256 byte aligned + * addresses just passing upper 32 bits of 40 bit address + * Additional reserved dwords for future use cases + */ + +/* + * Enum describes the various msduq/mpduq types, + * First 8 types correspond to the standard msduq types for data + * Next come custom flows for specific purposes + * enum 30 is reserved for mpduq type + */ +typedef enum { + HTT_H2T_TID_MSDUQ_NONUDP, /* 0 */ + HTT_H2T_TID_MSDUQ_UDP, /* 1 */ + HTT_H2T_TID_MSDUQ_CUSTOM_0, /* 2 */ + HTT_H2T_TID_MSDUQ_CUSTOM_1, /* 3 */ + HTT_H2T_TID_MSDUQ_CUSTOM_2, /* 4 */ + HTT_H2T_TID_MSDUQ_CUSTOM_3, /* 5 */ + HTT_H2T_TID_MSDUQ_CUSTOM_4, /* 6 */ + HTT_H2T_TID_MSDUQ_CUSTOM_5, /* 7 */ + + HTT_H2T_TID_MISC_MSDUQ_TYPE_START, /* 8 */ + HTT_H2T_TID_MSDUQ_HOL = HTT_H2T_TID_MISC_MSDUQ_TYPE_START, /* also 8 */ + HTT_H2T_TID_MSDUQ_MCAST, /* 9 */ + HTT_H2T_TID_MSDUQ_FAST_ROAMING, /* 10 */ + + HTT_H2T_TID_MSDUQ_DATA_TYPE_END = 29, /* 29 */ + HTT_H2T_TID_MPDUQ_TYPE, /* 30 */ + HTT_H2T_TID_MSDUQ_MPDUQ_TYPE_END, /* 31 */ +} HTT_H2T_TID_MSDUQ_MPDUQ_TYPE; + +/* + * Enum to denote tid nums that can be used + * first 8 {0 - 7} numbers correspond to data access category + * {8 - 15} are for user defined usecases + * 16 is used to denote the non-qos tid + */ +typedef enum { + HTT_H2T_DEFAULT_TID_NUM = 0, + HTT_H2T_MAX_VALID_DATA_TID_NUM = 7, + HTT_H2T_NON_QOS_TID_NUM = 16, + HTT_H2T_MAX_TID_NUM = 31, +} H2T_TX_TID; + +/* HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO */ +PREPACK struct htt_h2t_mpduq_or_msduq_info { + A_UINT32 msg_type: 8, /* bits 7:0 */ + msduq_mpduq_type: 5, /* bits 12:8 */ + hw_link_id: 4, /* bits 16:13 */ + reserved0: 15; /* bits 31:17 */ + + union { + struct { + A_UINT32 mpduq_address_39_8; /* bits 31:0 */ + A_UINT32 mpduq_number: 24, /* bits 23:0 */ + pn_addr_39_32: 8; /* bits 31:24 */ + A_UINT32 pn_addr_31_0; /* bits 31:0 */ + A_UINT32 reserved1a; /* bits 31:0 */ + A_UINT32 reserved1b; /* bits 31:0 */ + A_UINT32 reserved1c; /* bits 31:0 */ + A_UINT32 reserved1d; /* bits 31:0 */ + A_UINT32 reserved1e; /* bits 31:0 */ + A_UINT32 reserved1f; /* bits 31:0 */ + }; + struct { + A_UINT32 tx_msduq_number: 24, /* bits 23:0 */ + svc_class_id: 8; /* bits 31:24 */ + A_UINT32 msduq_address_39_8; /* bits 31:0 */ + A_UINT32 reserved2a; /* bits 31:0 */ + A_UINT32 reserved2b; /* bits 31:0 */ + A_UINT32 reserved2c; /* bits 31:0 */ + A_UINT32 reserved2d; /* bits 31:0 */ + A_UINT32 reserved2e; /* bits 31:0 */ + A_UINT32 reserved2f; /* bits 31:0 */ + A_UINT32 reserved2g; /* bits 31:0 */ + }; + }; +} POSTPACK; + +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_M 0x00001F00 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_S 8 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_MSDUQ_MPDUQ_TYPE_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_M 0x0001E000 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_S 13 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_INFO_HW_LINK_ID_S)); \ + } while (0) + + +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_M 0x00000FFF +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_S 0 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_PEER_ID_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_M 0x0001F000 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_S 12 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_TID_NUM_S)); \ + } while (0) + + +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_M 0x003E0000 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_S 17 +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_OR_MSDUQ_NUMBER_MSDUQ_MPDUQ_TYPE_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_M 0xFFFFFFFF +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_S 0 +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_ADDRESS_39_8_S)); \ + } while (0) + + +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_M 0x00FFFFFF +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_S 0 +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_GET(_var) \ + (((_var) & HTT_H2T_MSG_MPDUQ_INFO_MPDUQ_NUMBER_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_INFO_MPDUQ_NUMBER_S)); \ + } while (0) + + +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_M 0xFF000000 +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_S 24 +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_39_32_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_M 0xFFFFFFFF +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_S 0 +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_M) >> \ + HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_S) +#define HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MPDUQ_INFO_PN_ADDRESS_31_0_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_M 0x00FFFFFF +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_S 0 +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_M) >> \ + HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_S) +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MSDUQ_INFO_TX_MSDUQ_NUMBER_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_M 0xFF000000 +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_S 24 +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_M) >> \ + HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_S) +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MSDUQ_INFO_SVC_CLASS_ID_S)); \ + } while (0) + +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_M 0xFFFFFFFF +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_S 0 +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_GET(_var) \ + (((_var) & HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_M) >> \ + HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_S) +#define HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8, _val); \ + ((_var) |= ((_val) << HTT_H2T_MSG_TYPE_MSDUQ_INFO_MSDUQ_ADDRESS_39_8_S)); \ + } while (0) + + +/* + * @brief host -> target HTT_AST_INFO message + * + * MSG_TYPE => HTT_H2T_MSG_TYPE_AST_INFO + * + * The message would appear as follows: + * |31 24|23 21|20|19|18|17|16|15 8|7 0| + * |--------------+-------------------------------------+-------------------| + * |ast_max_search| ast_table_size | msg_type | + * |------------------------------------------------------------------------| + * | ast_base_addr_31_0 | + * |------------------------------------------------------------------------| + * | ase_hash_key1 | + * |------------------------------------------------------------------------| + * | ase_hash_key2 | + * |------------------------------------------------------------------------| + * | ase_hash_key3 | + * |--------------------+--+--+--+--+--+----------------+-------------------| + * | reserved |L |K |J |I |H | tmo |ast_base_addr_39_32| + * |--------------------+--+--+--+--+--+----------------+-------------------| + * + * The message is interpreted as follows: + * dword0 b'7:0 - msg_type + * 0 b'23:8 - ast table size + * b'31:24 - ast max search + * dword1 - b'31:0 - ast table base address + * dword2 - b'31:0 - ase hash key 1 + * dword3 - b'31:0 - ase hash key 2 + * dword4 - b'31:0 - ase hash key 3 + * dword5 - b'7:0 - ast_base_addr_39_32 + * b'15:8 - ast_timeout_threshold + * b'16 - H - ast cache disable knob + * b'17 - I - ast cache faluires disable knob + * b'18 - J - ast cache cmd read bypass + * Bypassing the reads from memory when an entry is not + * found in cache, in case of full cache commands, + * write back or invalidate commands. + * b'19 - K - ast cache write back fx + * If this fix is disabled, then any write back command + * for a cache line will also lead to invalidation of + * that cache line. + * b'20 - L - ast cache only entry command fix + * If enabled, a new cache entry will always be created + * for requests for which matching data was found + * neither in cache nor in memory. + */ +PREPACK struct htt_ast_info_t { + A_UINT32 msg_type: 8, + ast_table_size: 16, /* number of entries in AST */ + ast_max_search: 8; + A_UINT32 ast_base_addr; /* base address of the AST table */ + A_UINT32 ase_hash_key1; + A_UINT32 ase_hash_key2; + A_UINT32 ase_hash_key3; + A_UINT32 ast_base_addr_39_32: 8, /* 7:0 */ + ast_timeout_threshold: 8, /* 15:8 */ + ast_cache_disable: 1, /* 16 */ + ast_cache_failures_disable: 1, /* 17 */ + ast_cache_cmd_read_bypass_dis: 1, /* 18 */ + ast_cache_write_back_fix_dis: 1, /* 19 */ + ast_cache_only_entry_cmd_fix_dis: 1, /* 20 */ + reserved: 11; +} POSTPACK; + + +#define HTT_AST_INFO_SZ (sizeof(struct htt_ast_info_t)) + +/* DWORD0 */ +#define HTT_AST_INFO_AST_TABLE_SIZE_M 0x00ffff00 +#define HTT_AST_INFO_AST_TABLE_SIZE_S 8 +#define HTT_AST_INFO_AST_TABLE_SIZE_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_TABLE_SIZE_M) >> \ + HTT_AST_INFO_AST_TABLE_SIZE_S) +#define HTT_AST_INFO_AST_TABLE_SIZE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_TABLE_SIZE, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_TABLE_SIZE__S)); \ + } while (0) + +#define HTT_AST_INFO_AST_MAX_SEARCH_M 0xff000000 +#define HTT_AST_INFO_AST_MAX_SEARCH_S 24 +#define HTT_AST_INFO_AST_MAX_SEARCH_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_MAX_SEARCH_M) >> \ + HTT_AST_INFO_AST_MAX_SEARCH_S) +#define HTT_AST_INFO_AST_MAX_SEARCH_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_MAX_SEARCH, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_MAX_SEARCH_S)); \ + } while (0) + + +/* DWORD5 */ + +#define HTT_AST_INFO_AST_BASE_ADDR_39_32_M 0x000000ff +#define HTT_AST_INFO_AST_BASE_ADDR_39_32_S 0 +#define HTT_AST_INFO_AST_BASE_ADDR_39_32_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_BASE_ADDR_39_32_M) >> \ + HTT_AST_INFO_AST_BASE_ADDR_39_32_S) +#define HTT_AST_INFO_AST_BASE_ADDR_39_32_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_BASE_ADDR_39_32, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_BASE_ADDR_39_32_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_M 0x0000ff00 +#define HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_S 8 +#define HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_M) >> \ + HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_S) +#define HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_TIMEOUT_THRESHOLD, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_TIMEOUT_THRESHOLD_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_CACHE_DISABLE_M 0x00010000 +#define HTT_AST_INFO_AST_CACHE_DISABLE_s 16 +#define HTT_AST_INFO_AST_CACHE_DISABLE_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_CACHE_DISABLE_M) >> \ + HTT_AST_INFO_AST_CACHE_DISABLE_s) +#define HTT_AST_INFO_AST_CACHE_DISABLE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_CACHE_DISABLE, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_CACHE_DISABLE_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_M 0x00020000 +#define HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_s 17 +#define HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_M) >> \ + HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_s) +#define HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_CACHE_FALUIRES_DISABLE_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_M 0x00040000 +#define HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_s 18 +#define HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_M) >> \ + HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_s) +#define HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_CACHE_CMD_READ_BYPASS_DIS_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_M 0x00080000 +#define HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_s 19 +#define HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_M) >> \ + HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_s) +#define HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_CACHE_WRITE_BACK_FIX_DIS_S)); \ + } while (0) + +#define HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_M 0x00100000 +#define HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_s 20 +#define HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_GET(_var) \ + (((_var) & HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_M) >> \ + HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_s) +#define HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS, _val); \ + ((_var) |= ((_val) << HTT_AST_INFO_AST_CACHE_ONLY_ENTRY_CMD_FIX_DIS_S)); \ + } while (0) + /*=== target -> host messages ===============================================*/ @@ -11659,6 +12185,8 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_TX_LCE_SUPER_RULE_SETUP_DONE = 0x3b, HTT_T2H_MSG_TYPE_SDWF_MSDUQ_CFG_IND = 0x3c, HTT_T2H_MSG_TYPE_MLO_LATENCY_REQ = 0x3d, + HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP = 0x3e, + HTT_T2H_MSG_TYPE_HAPS = 0x3f, HTT_T2H_MSG_TYPE_TEST, @@ -14661,40 +15189,41 @@ PREPACK struct htt_tx_offload_deliver_ind_hdr_t * with, so that the host can use that MLO peer ID to determine which peer * transmitted the rx frame. * - * |31 |29 27|26 24|23 20|19 17|16|15 8|7 0| - * |-------------------------------------------------------------------------| - * |RSVD | PRC |NUMLINK| MLO peer ID | msg type | - * |-------------------------------------------------------------------------| - * | MAC addr 3 | MAC addr 2 | MAC addr 1 | MAC addr 0 | - * |-------------------------------------------------------------------------| - * | RSVD_16_31 | MAC addr 5 | MAC addr 4 | - * |-------------------------------------------------------------------------| - * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 0 | - * |-------------------------------------------------------------------------| - * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 1 | - * |-------------------------------------------------------------------------| - * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 2 | - * |-------------------------------------------------------------------------| - * |RSVD | - * |-------------------------------------------------------------------------| - * |RSVD | - * |-------------------------------------------------------------------------| - * | htt_tlv_hdr_t | - * |-------------------------------------------------------------------------| - * |RSVD_27_31 |CHIPID| VDEVID | SW peer ID | - * |-------------------------------------------------------------------------| - * | htt_tlv_hdr_t | - * |-------------------------------------------------------------------------| - * |RSVD_27_31 |CHIPID| VDEVID | SW peer ID | - * |-------------------------------------------------------------------------| - * | htt_tlv_hdr_t | - * |-------------------------------------------------------------------------| - * |RSVD_27_31 |CHIPID| VDEVID | SW peer ID | - * |-------------------------------------------------------------------------| + * |31 |29 27|26|25|24|23 20|19 17|16|15 8|7 0| + * |--------------------------------------------------------------------------| + * |RSVD | PRC | NUMLINK| MLO peer ID | msg type | + * |--------------------------------------------------------------------------| + * | MAC addr 3 | MAC addr 2 | MAC addr 1 | MAC addr 0 | + * |--------------------------------------------------------------------------| + * | RSVD_25_31 |CV| CLASS_INFO_IDX | MAC addr 5 | MAC addr 4 | + * |--------------------------------------------------------------------------| + * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 0 | + * |--------------------------------------------------------------------------| + * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 1 | + * |--------------------------------------------------------------------------| + * |CACHE_SET_NUM| TIDMASK |CHIPID|V| Primary TCL AST IDX 2 | + * |--------------------------------------------------------------------------| + * |RSVD | + * |--------------------------------------------------------------------------| + * |RSVD | + * |--------------------------------------------------------------------------| + * | htt_tlv_hdr_t | + * |--------------------------------------------------------------------------| + * |RSVD_27_31 | CHIPID | VDEVID | SW peer ID | + * |--------------------------------------------------------------------------| + * | htt_tlv_hdr_t | + * |--------------------------------------------------------------------------| + * |RSVD_27_31 | CHIPID | VDEVID | SW peer ID | + * |--------------------------------------------------------------------------| + * | htt_tlv_hdr_t | + * |--------------------------------------------------------------------------| + * |RSVD_27_31 | CHIPID | VDEVID | SW peer ID | + * |--------------------------------------------------------------------------| * * Where: * PRC - Primary REO CHIPID - 3 Bits Bit24,25,26 * NUMLINK - NUM_LOGICAL_LINKS - 3 Bits Bit27,28,29 + * CV - CLASSIFY_INFO_IDX_VALID - 1 Bit Bit24 * V (valid) - 1 Bit Bit17 * CHIPID - 3 Bits * TIDMASK - 8 Bits @@ -14734,6 +15263,16 @@ PREPACK struct htt_tx_offload_deliver_ind_hdr_t * Purpose: Identifies which peer node the peer ID is for. * Value: upper 2 bytes of peer node's MAC address * + * - CLASS_INFO_IDX + * Bits 23:16 + * Purpose: Classify info index assists TCL-L Block in certain families of + * WLAN chips to start finding the flow from the corresponding + * entry in the FLOW LOOK UP TABLE in MLO case + * - CV (CLASS_INFO_IDX_VALID) + * Bit 24 + * Purpose: if set indicates that the CLASS_INFO_IDX is valid, + * else ignore the value reported + * * - PRIMARY_TCL_AST_IDX * Bits 15:0 * Purpose: Primary TCL AST index for this peer. @@ -14805,6 +15344,11 @@ typedef enum { #define HTT_RX_MLO_PEER_MAP_MAC_ADDR_U16_M 0x0000ffff #define HTT_RX_MLO_PEER_MAP_MAC_ADDR_U16_S 0 +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_M 0x00ff0000 +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_S 16 +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_M 0x01000000 +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_S 24 + #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_M 0x0000ffff #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_S 0 #define HTT_RX_MLO_PEER_MAP_AST_INDEX_VALID_FLAG_M 0x00010000 @@ -14853,6 +15397,30 @@ typedef enum { #define HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_GET(word) \ (((word) & HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_M) >> HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_S) +#define HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_SET(word, value) \ + do { \ + HTT_CHECK_SET_VAL(HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID, value); \ + (word) |= (value) << HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_S; \ + } while (0) +#define HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_GET(word) \ + (((word) & HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_M) >> HTT_RX_MLO_PEER_PRIMARY_REO_CHIP_ID_S) + +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_SET(word, value) \ + do { \ + HTT_CHECK_SET_VAL(HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX, value); \ + (word) |= (value) << HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_S; \ + } while (0) +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_GET(word) \ + (((word) & HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_M) >> HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_S) + +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_SET(word, value) \ + do { \ + HTT_CHECK_SET_VAL(HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG, value); \ + (word) |= (value) << HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_S; \ + } while (0) +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_GET(word) \ + (((word) & HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_M) >> HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_S) + #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_SET(word, value) \ do { \ HTT_CHECK_SET_VAL(HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX, value); \ @@ -14935,6 +15503,8 @@ typedef enum { #define HTT_RX_MLO_PEER_MAP_MAC_ADDR_OFFSET 4 /* bytes */ +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_OFFSET 8 /* bytes */ +#define HTT_RX_MLO_PEER_MAP_CLASSIFY_INFO_IDX_VALID_FLAG_OFFSET 8 /* bytes */ #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_0_OFFSET 12 /* bytes */ #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_1_OFFSET 16 /* bytes */ #define HTT_RX_MLO_PEER_MAP_PRIMARY_AST_INDEX_2_OFFSET 20 /* bytes */ @@ -23449,4 +24019,177 @@ PREPACK struct htt_t2h_mlo_latency_req_t { } while (0) +/* MSG_TYPE => HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP + * + * The following diagram shows the format of the global peer unmap message sent + * from the target to the host. This message is used to send unmap event to host + * after tid and msduq/mpduq cleanup in FW, host cleans up msduq/mpduq based on + * message. + * + * |31 24|23 20|19 8|7 0| + * |-----------------------------------------------------------------------| + * | reserved | hw_link_id | global_peer_id | msg type | + * |-----------------------------------------------------------------------| + * @details + * struct htt_t2h_global_peer_id_unmap_t: + * + * The message is interpreted as follows: + * dword0 - b'7:0 - msg_type: Identifies a request for MLO latency stats + * This will be set to 0x3e + * (HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP) + * b'19:8 - global_peer_id : global peer id assigned by host + * b'23:20 - hw_link_id : hw link id for which unmap is being sent + * + */ + + +/* HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP */ +PREPACK struct htt_t2h_global_peer_id_unmap_t { + A_UINT32 msg_type: 8, /* bits 7:0 */ + global_peer_id: 12, /* bits 19:8 */ + hw_link_id: 4, /* bits 23:20 */ + reserved: 8; /* bits 31:16 */ +} POSTPACK; + +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_M 0x000FFF00 +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_S 8 +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_GET(_var) \ + (((_var) & HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_M) >> \ + HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_S) +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID, _val); \ + ((_var) |= ((_val) << HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_GLOBAL_PEER_ID_S)); \ + } while (0) + +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_M 0x00F00000 +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_S 20 +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_GET(_var) \ + (((_var) & HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_M) >> \ + HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_S) +#define HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID, _val); \ + ((_var) |= ((_val) << HTT_T2H_MSG_TYPE_GLOBAL_PEER_ID_UNMAP_HW_LINK_ID_S)); \ + } while (0) + + +/* + * @brief target -> pause/unpause host tx queues based on FW indication + * MSG_TYPE => HTT_T2H_MSG_TYPE_HAPS + * + * @details * Header fields: + * + * |31 22|21 20|19 16|15 8|7 0| + * |---------+-------------+-----------+------------------+---------------| + * | RSVD |time_type |action_code| vdev_id | msg type | + * |----------------------------------------------------------------------| + * | time_high | + * |----------------------------------------------------------------------| + * | time_low | + * |----------------------------------------------------------------------| + * + * dword0 - b'7:0 - msg_type: This will be set to + * 0x3f (HTT_T2H_MSG_TYPE_HAPS) + * b'15:8 - vdev_id + * b'19:16 - action_code (HTT_T2H_HAPS_ACTION_CODE): + * b'0000: Pause + * b'0001: Pause with One-Shot Unpause + * b'0010: Unpause + * values 3-15: reserved + * b'21:20 - time_type + * b'31:22 - rsvd + * reverse action_code at time specified below + * (units are specified by the type_type bitfield) + * dword1 - b'31:0 uint32_t time_high + * dword2 - b'31:0 uint32_t time_low + */ +typedef enum { + HTT_T2H_HAPS_ACTION_PAUSE = 0x00, + HTT_T2H_HAPS_ACTION_PAUSE_WITH_ONESHOT_UNPAUSE = 0x01, + HTT_T2H_HAPS_ACTION_UNPAUSE = 0x02, +} HTT_T2H_HAPS_ACTION_CODE; + +typedef enum { + HTT_T2H_HAPS_TIME_TYPE_HOST_QTIME = 0x00, + HTT_T2H_HAPS_TIME_TYPE_TSF = 0x01, +} HTT_T2H_HAPS_TIME_TYPE; + +PREPACK struct htt_t2h_power_state_info { + uint32_t msg_type : 8, /* [7:0] */ + vdev_id : 8, /* [15:8] */ + action_code : 4, /* [19:16] */ + time_type: 2, /* [21:20] */ + rsvd: 10; /* [31:22] */ + uint32_t time_low; + uint32_t time_high; +} POSTPACK; + +#define HTT_T2H_POWER_STATE_INFO_SIZE (sizeof(struct htt_t2h_power_state_info)) + +#define HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_M 0x0000FF00 +#define HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_S 8 + +#define HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_GET(_var) \ + (((_var) & HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_M) >> \ + HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_S) +#define HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID, _val); \ + ((_var) |= ((_val) << HTT_T2H_POWER_STATE_INFO_HTT_VDEV_ID_S));\ + } while (0) + +#define HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_M 0x000F0000 +#define HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_S 16 + +#define HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_GET(_var) \ + (((_var) & HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_M) >> \ + HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_S) +#define HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE, _val); \ + ((_var) |= ((_val) << HTT_T2H_POWER_STATE_INFO_HTT_ACTION_CODE_S));\ + } while (0) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_M 0x00300000 +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_S 20 + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_GET(_var) \ + (((_var) & HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_M) >> \ + HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_S) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE, _val); \ + ((_var) |= ((_val) << HTT_T2H_POWER_STATE_INFO_HTT_TIME_TYPE_S));\ + } while (0) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_M 0xFFFFFFFF +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_S 0 + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_GET(_var) \ + (((_var) & HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_M) >> \ + HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_S) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH, _val); \ + ((_var) |= ((_val) << HTT_T2H_POWER_STATE_INFO_HTT_TIME_HIGH_S));\ + } while (0) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_M 0xFFFFFFFF +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_S 0 + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_GET(_var) \ + (((_var) & HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_M) >> \ + HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_S) + +#define HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW, _val); \ + ((_var) |= ((_val) << HTT_T2H_POWER_STATE_INFO_HTT_TIME_LOW_S));\ + } while (0) + + + #endif diff --git a/drivers/staging/fw-api/fw/htt_ppdu_stats.h b/drivers/staging/fw-api/fw/htt_ppdu_stats.h index d1575d7c91ba..5f7bad98cb84 100644 --- a/drivers/staging/fw-api/fw/htt_ppdu_stats.h +++ b/drivers/staging/fw-api/fw/htt_ppdu_stats.h @@ -707,6 +707,58 @@ typedef enum HTT_PPDU_STATS_SPATIAL_REUSE HTT_PPDU_STATS_SPATIAL_REUSE; (((_val) & HTT_PPDU_STATS_COMMON_TRIG_COOKIE_M) >> \ HTT_PPDU_STATS_COMMON_TRIG_COOKIE_S) +#define HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_M 0x00000001 +#define HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_S 0 + +#define HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_GET(_var) \ + (((_var) & HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_M) >> \ + HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_S) + +#define HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE, _val); \ + ((_var) |= ((_val) << HTT_PPDU_STATS_COMMON_TLV_HTT_SEQ_TYPE_S)); \ + } while (0) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_M 0x00000002 +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_S 1 + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_GET(_var) \ + (((_var) & HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_M) >> \ + HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_S) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER, _val); \ + ((_var) |= ((_val) << HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BASIC_TRIGGER_S)); \ + } while (0) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_M 0x00000004 +#define HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_S 2 + +#define HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_GET(_var) \ + (((_var) & HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_M) >> \ + HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_S) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER, _val); \ + ((_var) |= ((_val) << HTT_PPDU_STATS_COMMON_TLV_IS_MANUAL_ULOFDMA_TRIGGER_S)); \ + } while (0) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_M 0x00000008 +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_S 3 + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_GET(_var) \ + (((_var) & HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_M) >> \ + HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_S) + +#define HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER, _val); \ + ((_var) |= ((_val) << HTT_PPDU_STATS_COMMON_TLV_IS_COMBINED_UL_BSRP_TRIGGER_S)); \ + } while (0) + enum HTT_SEQ_TYPE { WAL_PPDU_SEQ_TYPE = 0, HTT_PPDU_SEQ_TYPE = 1, @@ -902,26 +954,26 @@ typedef struct { * HTT_PPDU_SEQ_TYPE then decoder should interpret the * seq type as HTT_PPDU_STATS_SEQ_TYPE. * htt_seq_type field will be set to HTT_PPDU_SEQ_TYPE in - * firmware versions where this field is defined. - * BIT [31: 1] - reserved + * BIT [1 : 1] - is_combined_ul_basic_trigger - Flag to indicate if a + * given UL OFDMA/MU-MIMO Basic trigger is sent combined + * as part of existing DL data sequence. + * BIT [2 : 2] - is_manual_ulofdma_trigger - Flag to indicate if a + * given UL OFDMA trigger is manually triggered from the Host. + * BIT [3 : 3] - is_combined_ul_bsrp_trigger - Flag to indicate if a + * given UL BSRP trigger is sent combined as part of + * an existing DL/UL data sequence + * BIT [31: 4] - reserved */ union { A_UINT32 reserved__htt_seq_type; struct { A_UINT32 htt_seq_type: 1, - reserved3: 31; + is_combined_ul_basic_trigger: 1, + is_manual_ulofdma_trigger: 1, + is_combined_ul_bsrp_trigger: 1, + reserved3: 28; }; }; - /* is_manual_ulofdma_trigger: - * Flag to indicate if a given UL OFDMA trigger is manually triggered - * from the Host - */ - A_UINT32 is_manual_ulofdma_trigger; - /* is_combined_ul_bsrp_trigger: - * Flag to indicate if a given UL BSRP trigger is sent combined as - * part of existing DL/UL data sequence - */ - A_UINT32 is_combined_ul_bsrp_trigger; /* Flag to indicate if the channel chosen is 320_1 / 320_2 */ A_UINT32 chan_type_320mhz; } htt_ppdu_stats_common_tlv; diff --git a/drivers/staging/fw-api/fw/htt_stats.h b/drivers/staging/fw-api/fw/htt_stats.h index 5a57e7f9f171..c0730881c023 100644 --- a/drivers/staging/fw-api/fw/htt_stats.h +++ b/drivers/staging/fw-api/fw/htt_stats.h @@ -680,16 +680,18 @@ enum htt_dbg_ext_stats_type { HTT_DBG_ODD_PDEV_BE_TX_MU_OFDMA_STATS = HTT_DBG_EXT_STATS_ODD_PDEV_BE_TX_MU_OFDMA, - /** HTT_DBG_EXT_STATS_ODD_UL_BE_OFDMA + /** HTT_DBG_EXT_STATS_ODD_UL_BE_BN_OFDMA * PARAMS: * - No Params * RESP MSG: * - htt_rx_pdev_be_ul_ofdma_user_stats_tlv */ - HTT_DBG_EXT_STATS_ODD_UL_BE_OFDMA = 60, - /* retain the deprecated name as an alias */ + HTT_DBG_EXT_STATS_ODD_UL_BE_BN_OFDMA = 60, + /* retain deprecated names as aliases */ + HTT_DBG_EXT_STATS_ODD_UL_BE_OFDMA = + HTT_DBG_EXT_STATS_ODD_UL_BE_BN_OFDMA, HTT_DBG_ODD_UL_BE_OFDMA_STATS = - HTT_DBG_EXT_STATS_ODD_UL_BE_OFDMA, + HTT_DBG_EXT_STATS_ODD_UL_BE_BN_OFDMA, /** HTT_DBG_EXT_STATS_ODD_BE_TXBF_OFDMA */ @@ -829,6 +831,22 @@ enum htt_dbg_ext_stats_type { */ HTT_DBG_EXT_STATS_PDEV_UL_MUMIMO_ELIGIBLE = 74, + /** HTT_DBG_EXT_STATS_PAPRD_PB + * PARAMS: + * - No Params + * RESP MSG: + * - htt_stats_phy_paprd_pb_tlv + */ + HTT_DBG_EXT_STATS_PAPRD_PB = 75, + + /** HTT_DBG_EXT_STATS_HDS_PROF + * PARAMS: + * - No Params + * RESP MSG: + * - htt_stats_hds_prof_stats_tlv + */ + HTT_DBG_EXT_STATS_HDS_PROF = 76, + /* keep this last */ HTT_DBG_NUM_EXT_STATS = 256, @@ -915,11 +933,14 @@ typedef enum { HTT_TX_RATE_STATS_DEFAULT, /* - * Upload 11be OFDMA TX stats + * Upload 11be and 11bn OFDMA TX stats * * TLV: htt_tx_pdev_rate_stats_be_ofdma_tlv */ - HTT_TX_RATE_STATS_UPLOAD_11BE_OFDMA, + HTT_TX_RATE_STATS_UPLOAD_11BE_11BN_OFDMA, + /* retain prior name as an alias */ + HTT_TX_RATE_STATS_UPLOAD_11BE_OFDMA = + HTT_TX_RATE_STATS_UPLOAD_11BE_11BN_OFDMA, } htt_tx_rate_stats_upload_t; /* htt_rx_ul_trigger_stats_upload_t @@ -934,11 +955,14 @@ typedef enum { HTT_RX_UL_TRIGGER_STATS_UPLOAD_11AX_OFDMA, /* - * Upload 11be UL OFDMA RX Trigger stats + * Upload 11be and 11bn UL OFDMA RX Trigger stats * * TLV: htt_rx_pdev_be_ul_trigger_stats_tlv */ - HTT_RX_UL_TRIGGER_STATS_UPLOAD_11BE_OFDMA, + HTT_RX_UL_TRIGGER_STATS_UPLOAD_11BE_11BN_OFDMA, + /* retain prior name as an alias */ + HTT_RX_UL_TRIGGER_STATS_UPLOAD_11BE_OFDMA = + HTT_RX_UL_TRIGGER_STATS_UPLOAD_11BE_11BN_OFDMA, } htt_rx_ul_trigger_stats_upload_t; /* @@ -1002,7 +1026,12 @@ typedef enum { #define HTT_TX_HWQ_MAX_CMD_STALL_STATS 5 #define HTT_TX_HWQ_MAX_FES_RESULT_STATS 10 #define HTT_PDEV_STATS_PPDU_DUR_HIST_BINS 16 +#define HTT_PDEV_STATS_PPDU_DUR_HIST_EXT_BINS 6 #define HTT_PDEV_STATS_PPDU_DUR_HIST_INTERVAL_US 250 +/* Max seq ctrl can be active in txq at a given instant */ +#define HTT_PDEV_STATS_MAX_SEQ_CTRL_HIST 4 +/* For BE max active seq_ctrl that can be in HWQ */ +#define HTT_PDEV_STATS_MAX_ACTIVE_SEQ_IN_HWQ_HIST 2 typedef enum { HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, @@ -1053,7 +1082,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Num PPDUs queued to HW */ A_UINT32 hw_queued; /** Num PPDUs reaped from HW */ @@ -1235,6 +1270,33 @@ typedef struct { /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_pdev_cmn_tlv htt_tx_pdev_stats_cmn_tlv; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + union { + A_UINT32 pdev_id__word; + struct { + A_UINT32 + pdev_id: 8, + reserved: 24; + }; + }; + A_UINT32 pending_seq_on_sched_post_hist[HTT_PDEV_STATS_MAX_SEQ_CTRL_HIST]; +} htt_stats_tx_pdev_pending_seq_cnt_on_sched_post_hist_tlv; + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_M 0x000000ff +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_S 0 + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_GET(_var) \ + (((_var) & HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_M) >> \ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_S) + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID, _val); \ + ((_var) |= ((_val) << HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_PDEV_ID_S)); \ + } while (0) + + #define HTT_TX_PDEV_STATS_URRN_TLV_SZ(_num_elems) (sizeof(A_UINT32) * (_num_elems)) /* NOTE: Variable length TLV, use length spec to infer array size */ typedef struct { @@ -1257,6 +1319,13 @@ typedef struct { /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_pdev_flush_tlv htt_tx_pdev_stats_flush_tlv_v; +#define HTT_TX_PDEV_MDSB_MAX_NUM_USERS 8 +typedef struct { + htt_tlv_hdr_t tlv_hdr; + A_UINT32 pdev_id; + A_UINT32 mdsb_num_users_histogram[HTT_TX_PDEV_MDSB_MAX_NUM_USERS]; +} htt_stats_tx_pdev_mdsb_num_users_histogram_tlv; + #define HTT_TX_PDEV_STATS_MLO_ABORT_TLV_SZ(_num_elems) (sizeof(A_UINT32) * (_num_elems)) /* NOTE: Variable length TLV, use length spec to infer array size */ typedef struct { @@ -1461,6 +1530,7 @@ typedef htt_stats_pdev_ctrl_path_tx_stats_tlv htt_pdev_ctrl_path_tx_stats_tlv_v; * - HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG * - HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG * - HTT_STATS_MU_PPDU_DIST_TAG + * - HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_ON_SCHED_POST_HIST_TAG */ /* NOTE: * This structure is for documentation, and cannot be safely used directly. @@ -1478,6 +1548,8 @@ typedef struct _htt_tx_pdev_stats { htt_stats_tx_pdev_tried_mpdu_cnt_hist_tlv tried_mpdu_cnt_hist_tlv; htt_stats_pdev_ctrl_path_tx_stats_tlv ctrl_path_tx_tlv; htt_stats_mu_ppdu_dist_tlv mu_ppdu_dist_tlv; + htt_stats_tx_pdev_pending_seq_cnt_on_sched_post_hist_tlv + pending_seq_cnt_on_sched_post_hist_tlv; } htt_tx_pdev_stats_t; #endif /* ATH_TARGET */ @@ -1521,10 +1593,17 @@ typedef htt_stats_hw_wd_timeout_tlv htt_hw_stats_wd_timeout_tlv; typedef struct { htt_tlv_hdr_t tlv_hdr; - /* BIT [ 7 : 0] :- mac_id + /** + * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 tx_abort; A_UINT32 tx_abort_fail_count; A_UINT32 rx_abort; @@ -1591,10 +1670,17 @@ typedef htt_stats_hw_pdev_errs_tlv htt_hw_stats_pdev_errs_tlv; typedef struct { htt_tlv_hdr_t tlv_hdr; - /* BIT [ 7 : 0] :- mac_id + /** + * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 last_unpause_ppdu_id; A_UINT32 hwsch_unpause_wait_tqm_write; A_UINT32 hwsch_dummy_tlv_skipped; @@ -1727,7 +1813,15 @@ typedef struct _htt_msdu_flow_stats_tlv { * BIT [20 : 20] :- drop_rule * BIT [31 : 21] :- reserved */ - A_UINT32 tx_flow_no__tid_num__drop_rule; + union { + struct { + A_UINT32 tx_flow_number : 16; + A_UINT32 tid_num : 4; + A_UINT32 drop_rule : 1; + A_UINT32 reserved : 11; + }; + A_UINT32 tx_flow_no__tid_num__drop_rule; + }; A_UINT32 last_cycle_enqueue_count; A_UINT32 last_cycle_dequeue_count; A_UINT32 last_cycle_drop_count; @@ -1804,13 +1898,26 @@ typedef struct _htt_tx_tid_stats_tlv { * BIT [15 : 0] :- sw_peer_id * BIT [31 : 16] :- tid_num */ - A_UINT32 sw_peer_id__tid_num; + union { + struct { + A_UINT32 sw_peer_id : 16; + A_UINT32 tid_num : 16; + }; + A_UINT32 sw_peer_id__tid_num; + }; /** * BIT [ 7 : 0] :- num_sched_pending * BIT [15 : 8] :- num_ppdu_in_hwq * BIT [31 : 16] :- reserved */ - A_UINT32 num_sched_pending__num_ppdu_in_hwq; + union { + struct { + A_UINT32 num_sched_pending : 8; + A_UINT32 num_ppdu_in_hwq : 8; + A_UINT32 reserved : 16; + }; + A_UINT32 num_sched_pending__num_ppdu_in_hwq; + }; A_UINT32 tid_flags; /** per tid # of hw_queued ppdu */ A_UINT32 hw_queued; @@ -1840,13 +1947,26 @@ typedef struct _htt_tx_tid_stats_v1_tlv { * BIT [15 : 0] :- sw_peer_id * BIT [31 : 16] :- tid_num */ - A_UINT32 sw_peer_id__tid_num; + union { + struct { + A_UINT32 sw_peer_id : 16; + A_UINT32 tid_num : 16; + }; + A_UINT32 sw_peer_id__tid_num; + }; /** * BIT [ 7 : 0] :- num_sched_pending * BIT [15 : 8] :- num_ppdu_in_hwq * BIT [31 : 16] :- reserved */ - A_UINT32 num_sched_pending__num_ppdu_in_hwq; + union { + struct { + A_UINT32 num_sched_pending : 8; + A_UINT32 num_ppdu_in_hwq : 8; + A_UINT32 reserved : 16; + }; + A_UINT32 num_sched_pending__num_ppdu_in_hwq; + }; A_UINT32 tid_flags; /** Max qdepth in bytes reached by this tid */ A_UINT32 max_qdepth_bytes; @@ -1893,6 +2013,8 @@ typedef struct _htt_tx_tid_stats_v1_tlv { */ A_UINT32 head_msdu_tqm_timestamp_us; A_UINT32 head_msdu_tqm_latency_us; + A_UINT32 pause_module_id_ext; + A_UINT32 block_module_id_ext; } htt_stats_tx_tid_details_v1_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_tid_details_v1_tlv htt_tx_tid_stats_v1_tlv; @@ -1929,7 +2051,13 @@ typedef struct _htt_rx_tid_stats_tlv { * BIT [15 : 0] : sw_peer_id * BIT [31 : 16] : tid_num */ - A_UINT32 sw_peer_id__tid_num; + union { + struct { + A_UINT32 sw_peer_id : 16; + A_UINT32 tid_num : 16; + }; + A_UINT32 sw_peer_id__tid_num; + }; /** Stored as little endian */ A_UINT8 tid_name[MAX_HTT_TID_NAME]; /** @@ -1965,9 +2093,9 @@ typedef struct { A_UINT32 mpdu_cnt; /** Number of rx MSDU */ A_UINT32 msdu_cnt; - /** pause bitmap */ + /** lower 32 bits of pause bitmap */ A_UINT32 pause_bitmap; - /** block bitmap */ + /** lower 32 bits of block bitmap */ A_UINT32 block_bitmap; /** current timestamp */ A_UINT32 current_timestamp; @@ -1997,6 +2125,10 @@ typedef struct { A_UINT32 inactive_time; /** Number of MPDUs dropped after max retries */ A_UINT32 remove_mpdus_max_retries; + /** extension with upper 32 bits of pause bitmap */ + A_UINT32 pause_bitmap_ext; + /** extension with upper 32 bits of block bitmap */ + A_UINT32 block_bitmap_ext; } htt_stats_peer_stats_cmn_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_peer_stats_cmn_tlv htt_peer_stats_cmn_tlv; @@ -2016,6 +2148,22 @@ typedef htt_stats_peer_stats_cmn_tlv htt_peer_stats_cmn_tlv; #define HTT_PEER_DETAILS_SRC_INFO_M 0x00000fff #define HTT_PEER_DETAILS_SRC_INFO_S 0 +#define HTT_PEER_DETAILS_PEER_PS_ENTRY_M 0x000000ff +#define HTT_PEER_DETAILS_PEER_PS_ENTRY_S 0 +#define HTT_PEER_DETAILS_PEER_PS_EXIT_M 0x0000ff00 +#define HTT_PEER_DETAILS_PEER_PS_EXIT_S 8 +#define HTT_PEER_DETAILS_PEER_PSPOLL_TRIGGER_M 0x00ff0000 +#define HTT_PEER_DETAILS_PEER_PSPOLL_TRIGGER_S 16 +#define HTT_PEER_DETAILS_PEER_UAPSD_TRIGGER_M 0xff000000 +#define HTT_PEER_DETAILS_PEER_UAPSD_TRIGGER_S 24 + +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_0_M 0x000003ff +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_0_S 0 +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_1_M 0x000ffc00 +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_1_S 10 +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_2_M 0x3ff00000 +#define HTT_PEER_DETAILS_PEER_PS_HISTOGRAM_2_S 20 + #define HTT_PEER_DETAILS_SET(word, httsym, val) \ do { \ @@ -2061,6 +2209,34 @@ typedef struct { rsvd1 : 20; /* [31:12] */ }; }; + + /* Dword 10 */ + union { + A_UINT32 word__peer_ps_entry__peer_ps_exit__peer_pspoll_trigger_received__peer_uapsd_trigger_received; + struct { + A_UINT32 peer_ps_entry : 8, /* [7:0] */ + peer_ps_exit : 8, /* [15:8] */ + peer_pspoll_trigger_received : 8, /* [23:16] */ + peer_uapsd_trigger_received : 8; /* [31:24] */ + }; + }; + + /* Dword 11 */ + union { + A_UINT32 word__peer_ps_histogram_0__peer_ps_histogram_1__peer_ps_histogram_2; + struct { + /* + * This word holds 3 10-bit histograms of power-save durations: + * bits 9:0 - count of durations < 200 ms + * bits 19:10 - count of durations between 200 to 500 ms + * bits 29:20 - count of durations > 500 ms + */ + A_UINT32 peer_ps_histogram_0 : 10, /* [9:0] */ + peer_ps_histogram_1 : 10, /* [19:10] */ + peer_ps_histogram_2 : 10, /* [29:20] */ + rsvd2 : 2; /* [31:30] */ + }; + }; } htt_stats_peer_details_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_peer_details_tlv htt_peer_details_tlv; @@ -2072,6 +2248,21 @@ typedef htt_stats_peer_details_tlv htt_peer_details_tlv; #define HTT_STATS_PEER_DETAILS_SRC_INFO_GET(word) ((word >> 0) & 0xfff) +#define HTT_STATS_PEER_DETAILS_PEER_PS_ENTRY_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PS_ENTRY) +#define HTT_STATS_PEER_DETAILS_PEER_PS_EXIT_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PS_EXIT) +#define HTT_STATS_PEER_DETAILS_PEER_PSPOLL_TRIGGER_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PSPOLL_TRIGGER) +#define HTT_STATS_PEER_DETAILS_PEER_UAPSD_TRIGGER_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_UAPSD_TRIGGER) +#define HTT_STATS_PEER_DETAILS_PEER_PS_HISTOGRAM_0_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PS_HISTOGRAM_0) +#define HTT_STATS_PEER_DETAILS_PEER_PS_HISTOGRAM_1_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PS_HISTOGRAM_1) +#define HTT_STATS_PEER_DETAILS_PEER_PS_HISTOGRAM_2_GET(word) \ + HTT_PEER_DETAILS_GET(word, PEER_PS_HISTOGRAM_2) + typedef struct { htt_tlv_hdr_t tlv_hdr; A_UINT32 sw_peer_id; @@ -2208,6 +2399,9 @@ typedef struct _htt_tx_peer_rate_stats_tlv { A_UINT32 tx_bw_320mhz; /* MCS 14,15 */ A_UINT32 tx_mcs_ext_2[HTT_TX_PEER_STATS_NUM_EXTRA2_MCS_COUNTERS]; + A_UINT32 peer_tx_ppdu_cnt; + A_UINT32 peer_tx_mpdu_try_cnt; + A_UINT32 peer_tx_mpdu_success_cnt; } htt_stats_peer_tx_rate_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_peer_tx_rate_stats_tlv htt_tx_peer_rate_stats_tlv; @@ -2292,6 +2486,8 @@ typedef struct _htt_rx_peer_rate_stats_tlv { A_UINT32 rx_bw_320mhz; /* MCS 14,15 */ A_UINT32 rx_mcs_ext_2[HTT_RX_PEER_STATS_NUM_EXTRA2_MCS_COUNTERS]; + A_UINT32 tot_rx_ppdu_bytes; + A_UINT32 rx_mpdu_try_cnt; } htt_stats_peer_rx_rate_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_peer_rx_rate_stats_tlv htt_rx_peer_rate_stats_tlv; @@ -2778,6 +2974,58 @@ typedef struct { typedef htt_stats_tx_hwq_txop_used_cnt_hist_tlv htt_tx_hwq_txop_used_cnt_hist_tlv_v; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + union { + A_UINT32 pdev_id__word; + struct { + A_UINT32 + pdev_id: 8, + reserved: 24; + }; + }; + A_UINT32 active_seq_in_hwq_hist[HTT_PDEV_STATS_MAX_ACTIVE_SEQ_IN_HWQ_HIST]; +} htt_stats_tx_pdev_pending_seq_cnt_in_hwq_hist_tlv; + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_M 0x000000ff +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_S 0 + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_GET(_var) \ + (((_var) & HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_M) >> \ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_S) + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID, _val); \ + ((_var) |= ((_val) << HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_PDEV_ID_S)); \ + } while (0) + +typedef struct { + htt_tlv_hdr_t tlv_hdr; + union { + A_UINT32 pdev_id__word; + struct { + A_UINT32 + pdev_id: 8, + reserved: 24; + }; + }; + A_UINT32 active_seq_in_txq_hist[HTT_PDEV_STATS_MAX_SEQ_CTRL_HIST]; +} htt_stats_tx_pdev_pending_seq_cnt_in_txq_hist_tlv; + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_M 0x000000ff +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_S 0 + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_GET(_var) \ + (((_var) & HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_M) >> \ + HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_S) + +#define HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID, _val); \ + ((_var) |= ((_val) << HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_PDEV_ID_S)); \ + } while (0) + /* STATS_TYPE : HTT_DBG_EXT_STATS_PDEV_TX_HWQ * TLV_TAGS: * - HTT_STATS_STRING_TAG @@ -2788,6 +3036,8 @@ typedef htt_stats_tx_hwq_txop_used_cnt_hist_tlv * - HTT_STATS_TX_HWQ_FES_STATUS_TAG * - HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG * - HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG + * - HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_HWQ_HIST_TAG + * - HTT_STATS_TX_PDEV_PENDING_SEQ_CNT_IN_TXQ_HIST_TAG */ /* NOTE: * This structure is for documentation, and cannot be safely used directly. @@ -2808,6 +3058,10 @@ typedef struct _htt_tx_hwq_stats { htt_stats_tx_hwq_fes_status_tlv fes_stats_tlv; htt_stats_tx_hwq_tried_mpdu_cnt_hist_tlv tried_mpdu_tlv; htt_stats_tx_hwq_txop_used_cnt_hist_tlv txop_used_tlv; + htt_stats_tx_pdev_pending_seq_cnt_in_hwq_hist_tlv + active_pending_seq_cnt_in_hwq_hist_tlv; + htt_stats_tx_pdev_pending_seq_cnt_in_txq_hist_tlv + active_pending_seq_cnt_in_txq_hist_tlv; } htt_tx_hwq_stats_t; #endif /* ATH_TARGET */ @@ -2897,14 +3151,34 @@ typedef enum { #define HTT_MAX_NUM_SBT_INTR 4 +typedef enum { + HTT_RU_ALLOC_MODE_PF, + HTT_RU_ALLOC_MODE_QOS, + HTT_RU_ALLOC_MODE_STATIC, + HTT_RU_ALLOC_MODE_EQUAL, + HTT_RU_ALLOC_MODE_SIMPLIFIED, + + /* Reserving additional modes for future use */ + HTT_RU_ALLOC_MODE_RESERVED_1, + HTT_RU_ALLOC_MODE_RESERVED_2, + + HTT_RU_ALLOC_NUM_MODES +} HTT_RU_ALLOC_MODE; + typedef struct { htt_tlv_hdr_t tlv_hdr; - /* + /** * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** BAR sent out for SU transmission */ A_UINT32 su_bar; /** SW generated RTS frame sent */ @@ -2962,6 +3236,7 @@ typedef struct { * (Smart basic triggers are only used with intervals <= 40 ms.) */ A_UINT32 smart_basic_trig_sch_histogram[HTT_MAX_NUM_SBT_INTR]; + A_UINT32 ru_alloc_mode_cnt[HTT_RU_ALLOC_NUM_MODES]; } htt_stats_tx_selfgen_cmn_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_selfgen_cmn_stats_tlv htt_tx_selfgen_cmn_stats_tlv; @@ -3073,6 +3348,22 @@ typedef struct { A_UINT32 ax_mu_bar_trigger_per_ac[HTT_NUM_AC_WMM]; /** 11AX HE MU-BAR Trigger frames per AC completed with error(s) */ A_UINT32 ax_mu_bar_trigger_errors_per_ac[HTT_NUM_AC_WMM]; + /** 11AX HE MU Combined UL OFDMA Basic Trigger frame sent over the air */ + A_UINT32 combined_ax_ulofdma_trigger_tried[HTT_NUM_AC_WMM]; + /** 11AX HE MU Combined UL OFDMA Basic Trigger completed with error(s) */ + A_UINT32 combined_ax_ulofdma_trigger_err[HTT_NUM_AC_WMM]; + /** 11AX HE MU Standalone UL OFDMA Basic Trigger frame sent over the air */ + A_UINT32 standalone_ax_ulofdma_trigger_tried[HTT_NUM_AC_WMM]; + /** 11AX HE MU Standalone UL OFDMA Basic Trigger completed with error(s) */ + A_UINT32 standalone_ax_ulofdma_trigger_err[HTT_NUM_AC_WMM]; + /** 11AX HE MU Combined UL MU-MIMO Basic Trigger frame sent over the air */ + A_UINT32 combined_ax_ulmumimo_trigger_tried[HTT_NUM_AC_WMM]; + /** 11AX HE MU Combined UL MU-MIMO Basic Trigger completed with error(s) */ + A_UINT32 combined_ax_ulmumimo_trigger_err[HTT_NUM_AC_WMM]; + /** 11AX HE MU Standalone UL MU-MIMO Basic Trigger frame sent over the air*/ + A_UINT32 standalone_ax_ulmumimo_trigger_tried[HTT_NUM_AC_WMM]; + /** 11AX HE MU Standalone UL MU-MIMO Basic Trigger completed with error(s)*/ + A_UINT32 standalone_ax_ulmumimo_trigger_err[HTT_NUM_AC_WMM]; } htt_stats_tx_selfgen_ax_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_selfgen_ax_stats_tlv htt_tx_selfgen_ax_stats_tlv; @@ -3138,10 +3429,55 @@ typedef struct { A_UINT32 be_mu_bar_trigger_per_ac[HTT_NUM_AC_WMM]; /** 11BE EHT MU-BAR Trigger frames per AC completed with error(s) */ A_UINT32 be_mu_bar_trigger_errors_per_ac[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Combined UL OFDMA Basic Trigger frame sent over the air */ + A_UINT32 combined_be_ulofdma_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Combined UL OFDMA Basic Trigger completed with error(s) */ + A_UINT32 combined_be_ulofdma_trigger_err[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Standalone UL OFDMA Basic Trigger frame sent over the air */ + A_UINT32 standalone_be_ulofdma_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Standalone UL OFDMA Basic Trigger completed with error(s) */ + A_UINT32 standalone_be_ulofdma_trigger_err[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Combined UL MU-MIMO Basic Trigger frame sent over the air */ + A_UINT32 combined_be_ulmumimo_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Combined UL MU-MIMO Basic Trigger completed with error(s) */ + A_UINT32 combined_be_ulmumimo_trigger_err[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Standalone UL MU-MIMO Basic Trigger frame sent over the air */ + A_UINT32 standalone_be_ulmumimo_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BE EHT MU Standalone UL MU-MIMO Basic Trigger completed with error(s) */ + A_UINT32 standalone_be_ulmumimo_trigger_err[HTT_NUM_AC_WMM]; } htt_stats_tx_selfgen_be_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_selfgen_be_stats_tlv htt_tx_selfgen_be_stats_tlv; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + /** 11bn UHR MU Basic Trigger frame sent over the air */ + A_UINT32 bn_basic_trigger; + /** 11bn UHR MU BSRP Trigger frame sent over the air */ + A_UINT32 bn_bsr_trigger; + /** 11bn UHR MU BAR Trigger frame sent over the air */ + A_UINT32 bn_mu_bar_trigger; + /** 11bn UHR MU RTS Trigger frame sent over the air */ + A_UINT32 bn_mu_rts_trigger; + + /** 11BN UHR MU Combined Freq. BSRP Trigger frame sent over the air */ + A_UINT32 combined_bn_bsr_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BN UHR MU Combined Freq. BSRP Trigger completed with error(s) */ + A_UINT32 combined_bn_bsr_trigger_err[HTT_NUM_AC_WMM]; + /** 11BN UHR MU Standalone Freq. BSRP Trigger frame sent over the air */ + A_UINT32 standalone_bn_bsr_trigger_tried[HTT_NUM_AC_WMM]; + /** 11BN UHR MU Standalone Freq. BSRP Trigger completed with error(s) */ + A_UINT32 standalone_bn_bsr_trigger_err[HTT_NUM_AC_WMM]; + /** 11BN UHR Manual Single-User UL OFDMA Trigger frame sent over the air */ + A_UINT32 manual_bn_su_ulofdma_basic_trigger[HTT_NUM_AC_WMM]; + /** 11BN UHR Manual Single-User UL OFDMA Trigger completed with error(s) */ + A_UINT32 manual_bn_su_ulofdma_basic_trigger_err[HTT_NUM_AC_WMM]; + /** 11BN UHR Manual Multi-User UL OFDMA Trigger frame sent over the air */ + A_UINT32 manual_bn_mu_ulofdma_basic_trigger[HTT_NUM_AC_WMM]; + /** 11BN UHR Manual Multi-User UL OFDMA Trigger completed with error(s) */ + A_UINT32 manual_bn_mu_ulofdma_basic_trigger_err[HTT_NUM_AC_WMM]; +} htt_stats_tx_selfgen_bn_tlv; + typedef struct { /* DEPRECATED */ htt_tlv_hdr_t tlv_hdr; /** 11AX HE OFDMA NDPA frame queued to the HW */ @@ -3723,6 +4059,29 @@ typedef struct { /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_selfgen_be_err_stats_tlv htt_tx_selfgen_be_err_stats_tlv; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + /** 11BN UHR MU Basic Trigger frame completed with error(s) */ + A_UINT32 bn_basic_trigger_err; + /** 11BN UHR MU BSRP Trigger frame completed with error(s) */ + A_UINT32 bn_bsr_trigger_err; + /** 11BN UHR MU BAR Trigger frame completed with error(s) */ + A_UINT32 bn_mu_bar_trigger_err; + /** 11BN UHR MU RTS Trigger frame completed with error(s) */ + A_UINT32 bn_mu_rts_trigger_err; + + /** 11BN UHR MU OFDMA Basic Trigger frame completed with partial user response */ + A_UINT32 bn_basic_trigger_partial_resp; + /** 11BN UHR MU BSRP Trigger frame completed with partial user response */ + A_UINT32 bn_bsr_trigger_partial_resp; + /** 11BN UHR MU BAR Trigger frame completed with partial user response */ + A_UINT32 bn_mu_bar_trigger_partial_resp; + /** 11BN UHR MU RTS Trigger frame blocked due to partner link TX/RX(eMLSR) */ + A_UINT32 bn_mu_rts_trigger_blocked; + /** 11BN UHR MU BSR Trigger frame blocked due to partner link TX/RX(eMLSR) */ + A_UINT32 bn_bsr_trigger_blocked; +} htt_stats_tx_selfgen_bn_err_tlv; + /* * Scheduler completion status reason code. * (0) HTT_TXERR_NONE - No error (Success). @@ -3854,6 +4213,19 @@ typedef struct { typedef htt_stats_tx_selfgen_be_sched_status_stats_tlv htt_tx_selfgen_be_sched_status_stats_tlv; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + /** 11BN UHR MU BAR scheduler completion status reason code */ + A_UINT32 bn_mu_bar_sch_status[HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + /** 11BN UHR MU BAR scheduler error code */ + A_UINT32 bn_mu_bar_sch_flag_err[HTT_TX_SELFGEN_NUM_SCH_TSFLAG_ERROR_STATS]; + + /** 11BN UHR UL OFDMA Basic Trigger scheduler completion status reason code */ + A_UINT32 bn_basic_trig_sch_status[HTT_TX_PDEV_STATS_NUM_TX_ERR_STATUS]; + /** 11BN UHR UL OFDMA Basic Trigger scheduler error code */ + A_UINT32 bn_basic_trig_sch_flag_err[HTT_TX_SELFGEN_NUM_SCH_TSFLAG_ERROR_STATS]; +} htt_stats_tx_selfgen_bn_sched_status_tlv; + /* STATS_TYPE : HTT_DBG_EXT_STATS_TX_SELFGEN_INFO * TLV_TAGS: * - HTT_STATS_TX_SELFGEN_CMN_STATS_TAG @@ -3866,6 +4238,9 @@ typedef htt_stats_tx_selfgen_be_sched_status_stats_tlv * - HTT_STATS_TX_SELFGEN_BE_STATS_TAG * - HTT_STATS_TX_SELFGEN_BE_ERR_STATS_TAG * - HTT_STATS_TX_SELFGEN_BE_SCHED_STATUS_STATS_TAG + * - HTT_STATS_TX_SELFGEN_BN_TAG + * - HTT_STATS_TX_SELFGEN_BN_ERR_TAG + * - HTT_STATS_TX_SELFGEN_BN_SCHED_STATUS_TAG */ /* NOTE: * This structure is for documentation, and cannot be safely used directly. @@ -3883,6 +4258,9 @@ typedef struct { htt_stats_tx_selfgen_be_stats_tlv be_tlv; htt_stats_tx_selfgen_be_err_stats_tlv be_err_tlv; htt_stats_tx_selfgen_be_sched_status_stats_tlv be_sched_status_tlv; + htt_stats_tx_selfgen_bn_tlv bn_tlv; + htt_stats_tx_selfgen_bn_err_tlv bn_err_tlv; + htt_stats_tx_selfgen_bn_sched_status_tlv bn_sched_status_tlv; } htt_tx_pdev_selfgen_stats_t; #endif /* ATH_TARGET */ @@ -4202,8 +4580,8 @@ typedef struct { /* NOTE: Variable length TLV, use length spec to infer array size */ typedef struct { htt_tlv_hdr_t tlv_hdr; - /** Scheduler command posted per tx_mode */ - A_UINT32 sched_cmd_posted[1/* length = num tx modes */]; + /** Scheduler command posted per tx_mode (length = num tx modes) */ + HTT_STATS_VAR_LEN_ARRAY1(A_UINT32, sched_cmd_posted); } htt_stats_sched_txq_cmd_posted_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_sched_txq_cmd_posted_tlv htt_sched_txq_cmd_posted_tlv_v; @@ -4213,8 +4591,8 @@ typedef htt_stats_sched_txq_cmd_posted_tlv htt_sched_txq_cmd_posted_tlv_v; /* NOTE: Variable length TLV, use length spec to infer array size */ typedef struct { htt_tlv_hdr_t tlv_hdr; - /** Scheduler command reaped per tx_mode */ - A_UINT32 sched_cmd_reaped[1/* length = num tx modes */]; + /** Scheduler command reaped per tx_mode (length = num tx modes) */ + HTT_STATS_VAR_LEN_ARRAY1(A_UINT32, sched_cmd_reaped); } htt_stats_sched_txq_cmd_reaped_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_sched_txq_cmd_reaped_tlv htt_sched_txq_cmd_reaped_tlv_v; @@ -4333,12 +4711,50 @@ typedef struct { * These supercycle trigger counts are not automatically reset, but * are reset upon request. */ - A_UINT32 supercycle_triggers[1/*HTT_SCHED_SUPERCYCLE_TRIGGER_MAX*/]; + HTT_STATS_VAR_LEN_ARRAY1(A_UINT32, supercycle_triggers); } htt_stats_sched_txq_supercycle_trigger_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_sched_txq_supercycle_trigger_tlv htt_sched_txq_supercycle_triggers_tlv_v; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + union { + A_UINT32 pdev_id__word; + struct { + A_UINT32 + pdev_id: 8, + reserved: 24; + }; + }; + A_UINT32 ist_txop_end_indicated_cnt; + A_UINT32 ist_txop_end_notify_at_cmd_status_end; + A_UINT32 ist_txop_end_notify_at_isr_end; + A_UINT32 sched_cmd_post_skip_on_seq_unavail; + A_UINT32 ist_txop_end_skip_on_seq_unavail; + A_UINT32 ist_txop_end_skip_on_mpdu_ownership; + A_UINT32 skip_early_schedule_due_to_per; + A_UINT32 sched_cmd_posted_at_hw_txop_end; + A_UINT32 sched_cmd_missed_at_hw_txop_end; + A_UINT32 sched_cmd_posted_at_sched_cmd_compl; + A_UINT32 sched_cmd_missed_at_sched_cmd_compl; + A_UINT32 num_QoS_sched_runs; +} htt_stats_sched_txq_early_compl_tlv; + +#define HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_M 0x000000ff +#define HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_S 0 + +#define HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_GET(_var) \ + (((_var) & HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_M) >> \ + HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_S) + +#define HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID, _val); \ + ((_var) |= ((_val) << HTT_STATS_SCHED_TXQ_EARLY_COMPL_PDEV_ID_S)); \ + } while (0) + + #define HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID_M 0x000000ff #define HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID_S 0 @@ -4458,7 +4874,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Current timestamp */ A_UINT32 current_timestamp; } htt_stats_tx_sched_cmn_tlv; @@ -4472,22 +4894,27 @@ typedef struct { * - HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG * - HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG * - HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG + * - HTT_STATS_SCHED_TXQ_EARLY_COMPL_TAG */ /* NOTE: * This structure is for documentation, and cannot be safely used directly. * Instead, use the constituent TLV structures to fill/parse. */ +#ifdef ATH_TARGET typedef struct { htt_stats_tx_sched_cmn_tlv cmn_tlv; struct { - htt_stats_tx_pdev_scheduler_txq_stats_tlv txq_tlv; - htt_stats_sched_txq_cmd_posted_tlv cmd_posted_tlv; - htt_stats_sched_txq_cmd_reaped_tlv cmd_reaped_tlv; + htt_stats_tx_pdev_scheduler_txq_stats_tlv txq_tlv; + htt_stats_sched_txq_cmd_posted_tlv cmd_posted_tlv; + htt_stats_sched_txq_cmd_reaped_tlv cmd_reaped_tlv; htt_stats_sched_txq_sched_order_su_tlv sched_order_su_tlv; htt_stats_sched_txq_sched_ineligibility_tlv sched_ineligibility_tlv; - htt_stats_sched_txq_supercycle_trigger_tlv htt_sched_txq_sched_ineligibility_tlv_esched_supercycle_trigger_tlv; + htt_stats_sched_txq_supercycle_trigger_tlv + htt_sched_txq_sched_ineligibility_tlv_esched_supercycle_trigger_tlv; + htt_stats_sched_txq_early_compl_tlv early_compl_tlv; } txq[1]; } htt_stats_tx_sched_t; +#endif /* == TQM STATS == */ @@ -4603,7 +5030,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 max_cmdq_id; A_UINT32 list_mpdu_cnt_hist_intvl; @@ -4719,7 +5152,14 @@ typedef struct { * BIT [15 : 8] :- cmdq_id * BIT [31 : 16] :- reserved */ - A_UINT32 mac_id__cmdq_id__word; + union { + struct { + A_UINT32 mac_id : 8; + A_UINT32 cmdq_id : 8; + A_UINT32 reserved : 16; + }; + A_UINT32 mac_id__cmdq_id__word; + }; A_UINT32 sync_cmd; A_UINT32 write_cmd; A_UINT32 gen_mpdu_cmd; @@ -4955,7 +5395,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /* Global Stats */ A_UINT32 tcl2fw_entry_count; @@ -5163,23 +5609,47 @@ typedef struct { * BIT [15 : 0] :- num_elems * BIT [31 : 16] :- prefetch_tail_idx */ - A_UINT32 num_elems__prefetch_tail_idx; + union { + struct { + A_UINT32 num_elems : 16; + A_UINT32 prefetch_tail_idx : 16; + }; + A_UINT32 num_elems__prefetch_tail_idx; + }; /** * BIT [15 : 0] :- head_idx * BIT [31 : 16] :- tail_idx */ - A_UINT32 head_idx__tail_idx; + union { + struct { + A_UINT32 head_idx : 16; + A_UINT32 tail_idx : 16; + }; + A_UINT32 head_idx__tail_idx; + }; /** * BIT [15 : 0] :- shadow_head_idx * BIT [31 : 16] :- shadow_tail_idx */ - A_UINT32 shadow_head_idx__shadow_tail_idx; + union { + struct { + A_UINT32 shadow_head_idx : 16; + A_UINT32 shadow_tail_idx : 16; + }; + A_UINT32 shadow_head_idx__shadow_tail_idx; + }; A_UINT32 num_tail_incr; /** * BIT [15 : 0] :- lwm_thresh * BIT [31 : 16] :- hwm_thresh */ - A_UINT32 lwm_thresh__hwm_thresh; + union { + struct { + A_UINT32 lwm_thresh : 16; + A_UINT32 hwm_thresh : 16; + }; + A_UINT32 lwm_thresh__hwm_thresh; + }; A_UINT32 overrun_hit_count; A_UINT32 underrun_hit_count; A_UINT32 prod_blockwait_count; @@ -5210,7 +5680,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 num_records; } htt_stats_ring_if_cmn_tlv; /* preserve old name alias for new name consistent with the tag name */ @@ -5290,7 +5766,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** * Indicates the total number of 128 byte buffers in the CMEM * that are available for buffer sharing @@ -5504,7 +5986,16 @@ typedef struct { * BIT [24 : 24] :- EP 0 -consumer, 1 - producer * BIT [31 : 25] :- reserved */ - A_UINT32 mac_id__ring_id__arena__ep; + union { + struct { + A_UINT32 mac_id : 8; + A_UINT32 ring_id : 8; + A_UINT32 arena : 8; + A_UINT32 ep : 1; + A_UINT32 reserved : 7; + }; + A_UINT32 mac_id__ring_id__arena__ep; + }; /** DWORD aligned base memory address of the ring */ A_UINT32 base_addr_lsb; A_UINT32 base_addr_msb; @@ -5518,25 +6009,49 @@ typedef struct { * BIT [15 : 0] :- num_avail_words * BIT [31 : 16] :- num_valid_words */ - A_UINT32 num_avail_words__num_valid_words; + union { + struct { + A_UINT32 num_avail_words : 16; + A_UINT32 num_valid_words : 16; + }; + A_UINT32 num_avail_words__num_valid_words; + }; /** Index of head and tail * BIT [15 : 0] :- head_ptr * BIT [31 : 16] :- tail_ptr */ - A_UINT32 head_ptr__tail_ptr; + union { + struct { + A_UINT32 head_ptr : 16; + A_UINT32 tail_ptr : 16; + }; + A_UINT32 head_ptr__tail_ptr; + }; /** Empty or full counter of rings * BIT [15 : 0] :- consumer_empty * BIT [31 : 16] :- producer_full */ - A_UINT32 consumer_empty__producer_full; + union { + struct { + A_UINT32 consumer_empty : 16; + A_UINT32 producer_full : 16; + }; + A_UINT32 consumer_empty__producer_full; + }; /** Prefetch status of consumer ring * BIT [15 : 0] :- prefetch_count * BIT [31 : 16] :- internal_tail_ptr */ - A_UINT32 prefetch_count__internal_tail_ptr; + union { + struct { + A_UINT32 prefetch_count : 16; + A_UINT32 internal_tail_ptr : 16; + }; + A_UINT32 prefetch_count__internal_tail_ptr; + }; } htt_stats_sring_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_sring_stats_tlv htt_sring_stats_tlv; @@ -5627,9 +6142,13 @@ typedef enum { /* 11be related updates */ #define HTT_TX_PDEV_STATS_NUM_BE_MCS_COUNTERS 16 /* 0...13,-2,-1 */ #define HTT_TX_PDEV_STATS_NUM_BE_BW_COUNTERS 5 /* 20,40,80,160,320 MHz */ +/* 11bn MCS counters: all BE MCS indices and 4 UHR iMCS */ +#define HTT_TX_PDEV_STATS_NUM_BN_MCS_COUNTERS 20 +#define HTT_TX_PDEV_STATS_NUM_BN_BW_COUNTERS 5 /* 20,40,80,160,320 MHz */ #define HTT_TX_PDEV_STATS_NUM_HE_SIG_B_MCS_COUNTERS 6 #define HTT_TX_PDEV_STATS_NUM_EHT_SIG_MCS_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_UHR_SIG_MCS_COUNTERS 4 typedef enum { HTT_TX_PDEV_STATS_AX_RU_SIZE_26, @@ -5662,6 +6181,9 @@ typedef enum { HTT_TX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS, } HTT_TX_PDEV_STATS_BE_RU_SIZE; +#define HTT_TX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS \ + HTT_TX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS + typedef struct { htt_tlv_hdr_t tlv_hdr; @@ -5669,7 +6191,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Number of tx ldpc packets */ A_UINT32 tx_ldpc; /** Number of tx rts packets */ @@ -5884,7 +6412,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** 11BE EHT DL MU OFDMA LDPC count */ A_UINT32 be_ofdma_tx_ldpc; @@ -5902,10 +6436,32 @@ typedef struct { A_UINT32 be_ofdma_tx_ru_size[HTT_TX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS]; /** 11BE EHT DL MU OFDMA EHT-SIG MCS stats */ A_UINT32 be_ofdma_eht_sig_mcs[HTT_TX_PDEV_STATS_NUM_EHT_SIG_MCS_COUNTERS]; + /** 11BE UHT DL MU OFDMA BA RU size stats */ A_UINT32 be_ofdma_ba_ru_size[HTT_TX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS]; -} htt_stats_tx_pdev_rate_stats_be_ofdma_tlv; -/* preserve old name alias for new name consistent with the tag name */ -typedef htt_stats_tx_pdev_rate_stats_be_ofdma_tlv + + /** 11BN UHR DL MU OFDMA LDPC count */ + A_UINT32 bn_ofdma_tx_ldpc; + /** 11BE UHR DL MU OFDMA TX MCS stats */ + A_UINT32 bn_ofdma_tx_mcs[HTT_TX_PDEV_STATS_NUM_BN_MCS_COUNTERS]; + /** + * 11BN UHR DL MU OFDMA TX NSS stats (Indicates NSS for individual users) + */ + A_UINT32 bn_ofdma_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + /** 11BN UHR DL MU OFDMA TX BW stats */ + A_UINT32 bn_ofdma_tx_bw[HTT_TX_PDEV_STATS_NUM_BN_BW_COUNTERS]; + /** 11BN UHR DL MU OFDMA TX guard interval stats */ + A_UINT32 bn_ofdma_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS][HTT_TX_PDEV_STATS_NUM_BN_MCS_COUNTERS]; + /** 11BN UHR DL MU OFDMA TX RU Size stats */ + A_UINT32 bn_ofdma_tx_ru_size[HTT_TX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS]; + /** 11BN UHR DL MU OFDMA UHR-SIG MCS stats */ + A_UINT32 bn_ofdma_uhr_sig_mcs[HTT_TX_PDEV_STATS_NUM_UHR_SIG_MCS_COUNTERS]; + /** 11BN UHR DL MU OFDMA BA RU size stats */ + A_UINT32 bn_ofdma_ba_ru_size[HTT_TX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS]; +} htt_stats_tx_pdev_rate_be_bn_ofdma_tlv; +/* preserve old names as aliases */ +typedef htt_stats_tx_pdev_rate_be_bn_ofdma_tlv + htt_stats_tx_pdev_rate_stats_be_ofdma_tlv; +typedef htt_stats_tx_pdev_rate_be_bn_ofdma_tlv htt_tx_pdev_rate_stats_be_ofdma_tlv; typedef struct { @@ -5913,6 +6469,8 @@ typedef struct { /** tx_ppdu_dur_hist: * Tx PPDU duration histogram, which holds the tx duration of PPDUs * under histogram bins of interval 250us + * + * Note that this histogram is extended by tx_ppdu_dur_hist_ext[] below. */ A_UINT32 tx_ppdu_dur_hist[HTT_PDEV_STATS_PPDU_DUR_HIST_BINS]; A_UINT32 tx_success_time_us_low; @@ -5926,6 +6484,11 @@ typedef struct { * OFDMA PPDUs under histogram bins of interval 250us */ A_UINT32 tx_ofdma_ppdu_dur_hist[HTT_PDEV_STATS_PPDU_DUR_HIST_BINS]; + /* tx_ppdu_dur_hist_ext: + * This array extends the PPDU duration histogram contained in the + * tx_ppdu_dur_hist[] array from 4 ms to 5.5 ms. + */ + A_UINT32 tx_ppdu_dur_hist_ext[HTT_PDEV_STATS_PPDU_DUR_HIST_EXT_BINS]; } htt_stats_tx_pdev_ppdu_dur_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_tx_pdev_ppdu_dur_tlv htt_tx_pdev_ppdu_dur_stats_tlv; @@ -5969,6 +6532,9 @@ typedef struct { #define HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_PER_NSS 16 #define HTT_RX_PDEV_STATS_NUM_BE_MCS_COUNTERS 16 /* 0-13, -2, -1 */ #define HTT_RX_PDEV_STATS_NUM_BE_BW_COUNTERS 5 /* 20,40,80,160,320 MHz */ +/* 802.11BN MCS: all 16 EHT MCS indices and 4 UHR iMCS */ +#define HTT_RX_PDEV_STATS_NUM_BN_MCS_COUNTERS 20 +#define HTT_RX_PDEV_STATS_NUM_BN_BW_COUNTERS 5 /* 20,40,80,160,320 MHz */ /* HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS: * RU size index 0: HTT_UL_OFDMA_V0_RU_SIZE_RU_26 @@ -6010,6 +6576,9 @@ typedef enum { HTT_RX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS, } HTT_RX_PDEV_STATS_BE_RU_SIZE; +#define HTT_RX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS \ + HTT_RX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS + #define HTT_RX_PDEV_RATE_STATS_MAC_ID_M 0x000000ff #define HTT_RX_PDEV_RATE_STATS_MAC_ID_S 0 @@ -6042,7 +6611,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 nsts; /** Number of rx ldpc packets */ @@ -6307,7 +6882,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 rx_11ax_ul_ofdma; @@ -6381,7 +6962,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 rx_11be_ul_ofdma; @@ -6438,14 +7025,59 @@ typedef struct { A_UINT32 ul_mlo_proc_qdepth_params_count; A_UINT32 ul_mlo_proc_accepted_qdepth_params_count; A_UINT32 ul_mlo_proc_discarded_qdepth_params_count; -} htt_stats_rx_pdev_be_ul_trig_stats_tlv; -/* preserve old name alias for new name consistent with the tag name */ -typedef htt_stats_rx_pdev_be_ul_trig_stats_tlv + + A_UINT32 rx_11bn_ul_ofdma; + + A_UINT32 bn_ul_ofdma_rx_mcs[HTT_RX_PDEV_STATS_NUM_BN_MCS_COUNTERS]; + A_UINT32 bn_ul_ofdma_rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS][HTT_RX_PDEV_STATS_NUM_BN_MCS_COUNTERS]; + A_UINT32 bn_ul_ofdma_rx_nss[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + A_UINT32 bn_ul_ofdma_rx_bw[HTT_RX_PDEV_STATS_NUM_BN_BW_COUNTERS]; + A_UINT32 bn_ul_ofdma_rx_stbc; + A_UINT32 bn_ul_ofdma_rx_ldpc; + + /* + * These are arrays to hold the number of PPDUs that we received per RU. + * E.g. PPDUs (data or non data) received in RU26 will be incremented in + * array offset 0 and similarly RU52 will be incremented in array offset 1 + */ + /** PPDU level */ + A_UINT32 bn_rx_ulofdma_data_ru_size_ppdu[HTT_RX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS]; + /** PPDU level */ + A_UINT32 bn_rx_ulofdma_non_data_ru_size_ppdu[HTT_RX_PDEV_STATS_NUM_BN_RU_SIZE_COUNTERS]; + + /** + * STA AID array for identifying which STA the + * Target-RSSI / FD-RSSI / pwr headroom stats are for + */ + A_UINT32 bn_uplink_sta_aid[HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + /** + * Trig Target RSSI for STA AID in same index - UNIT(dBm) + */ + A_INT32 bn_uplink_sta_target_rssi[HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + /** + * Trig FD RSSI from STA AID in same index - UNIT(dBm) + */ + A_INT32 bn_uplink_sta_fd_rssi[HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + /** + * Trig power headroom for STA AID in same idx - UNIT(dB) + */ + A_UINT32 bn_uplink_sta_power_headroom[HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + + /* + * Number of UHR UL OFDMA per-user responses containing only a QoS null in + * response to basic trigger. Typically a data response is expected. + */ + A_UINT32 bn_ul_ofdma_basic_trigger_rx_qos_null_only; +} htt_stats_rx_pdev_be_bn_ul_trig_tlv; +/* preserve old names as aliases */ +typedef htt_stats_rx_pdev_be_bn_ul_trig_tlv + htt_stats_rx_pdev_be_ul_trig_stats_tlv; +typedef htt_stats_rx_pdev_be_bn_ul_trig_tlv htt_rx_pdev_be_ul_trigger_stats_tlv; /* STATS_TYPE : HTT_DBG_EXT_STATS_PDEV_UL_TRIG_STATS * TLV_TAGS: - * - HTT_STATS_RX_PDEV_BE_UL_TRIG_STATS_TAG + * - HTT_STATS_RX_PDEV_BE_BN_UL_TRIG_TAG * NOTE: * This structure is for documentation, and cannot be safely used directly. * Instead, use the constituent TLV structures to fill/parse. @@ -6494,6 +7126,22 @@ typedef struct { typedef htt_stats_rx_pdev_be_ul_ofdma_user_stats_tlv htt_rx_pdev_be_ul_ofdma_user_stats_tlv; +typedef struct { + htt_tlv_hdr_t tlv_hdr; + + A_UINT32 user_index; + /** PPDU level */ + A_UINT32 bn_rx_ulofdma_non_data_ppdu; + /** PPDU level */ + A_UINT32 bn_rx_ulofdma_data_ppdu; + /** MPDU level */ + A_UINT32 bn_rx_ulofdma_mpdu_ok; + /** MPDU level */ + A_UINT32 bn_rx_ulofdma_mpdu_fail; + A_UINT32 bn_rx_ulofdma_non_data_nusers; + A_UINT32 bn_rx_ulofdma_data_nusers; +} htt_stats_rx_pdev_bn_ul_ofdma_user_tlv; + typedef struct { htt_tlv_hdr_t tlv_hdr; @@ -6534,12 +7182,18 @@ typedef struct { htt_tlv_hdr_t tlv_hdr; /** - * BIT [7:0] :- mac_id - * BIT [31:8] :- reserved + * BIT [ 7 : 0] :- mac_id + * BIT [31 : 8] :- reserved * * Refer to HTT_STATS_CMN_MAC_ID_GET/SET macros. */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Number of times UL MUMIMO RX packets received */ A_UINT32 rx_11ax_ul_mumimo; @@ -6593,12 +7247,18 @@ typedef struct { htt_tlv_hdr_t tlv_hdr; /** - * BIT [7:0] :- mac_id - * BIT [31:8] :- reserved + * BIT [ 7 : 0] :- mac_id + * BIT [31 : 8] :- reserved * * Refer to HTT_STATS_CMN_MAC_ID_GET/SET macros. */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Number of times UL MUMIMO RX packets received */ A_UINT32 rx_11be_ul_mumimo; @@ -6851,7 +7511,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Num PPDU status processed from HW */ A_UINT32 ppdu_recvd; /** Num MPDU across PPDUs with FCS ok */ @@ -6982,7 +7648,13 @@ typedef struct { * BIT [ 7 : 0] :- mac_id * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; /** Num of phy err */ A_UINT32 total_phy_err_cnt; /** Counts of different types of phy errs @@ -8135,6 +8807,8 @@ typedef struct { A_UINT32 ru_type; /* refer to htt_stats_ru_type enum */ htt_tx_rate_stats_t per_ru[HTT_TX_PDEV_STATS_NUM_BE_RU_SIZE_COUNTERS]; + + htt_tx_rate_stats_t per_tx_su_punctured_mode[HTT_TX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS]; } htt_stats_per_rate_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_per_rate_stats_tlv htt_tx_rate_stats_per_tlv; @@ -10464,6 +11138,123 @@ typedef struct { } htt_vdevs_txrx_stats_t; #endif /* ATH_TARGET */ +/* PAPRD and power boost stats and counters */ +typedef struct { + htt_tlv_hdr_t tlv_hdr; + + /** current pdev_id */ + A_UINT32 pdev_id; + /** DPD and PowerBoost trigger count */ + A_UINT32 total_dpd_cal_count; + A_UINT32 chan_change_dpd_cal_count; + A_UINT32 thermal_dpd_cal_count; + A_UINT32 recovery_dpd_cal_count; + A_UINT32 pb_cal_count; + /** DPD and PowerBoost Fail Count */ + A_UINT32 total_dpd_fail_count; + A_UINT32 chan_change_dpd_fail_count; + A_UINT32 thermal_dpd_fail_count; + A_UINT32 recovery_dpd_fail_count; + A_UINT32 pb_fail_count; + + /* + * DPD and Power Boost validity status + * + * BIT 0 - DPD_CAL_STATUS + * BIT 1 - PB_CAL_STATUS + * + * CAL_STATUS can be interpreted as below + * CAL_SUCCESS = 1 + * CAL_FAIL = 0 + */ + union { + A_UINT32 dpd_pb_validity_status; + struct { + A_UINT32 is_dpd_valid:1, + is_pb_valid:1, + rsvd:30; + }; + }; + + /** Last DPD cal time in ms */ + A_UINT32 last_dpd_cal_time; + + /** Last Power Boost cal time in ms */ + A_UINT32 last_pb_cal_time; + + /** Power Boost gain per BW and MCS, in 0.25 dB units + * For example, a value of 2 represents a 0.5 dB gain. + */ + A_UINT32 power_boost_gain[HTT_TX_PDEV_STATS_NUM_BE_BW_COUNTERS][HTT_TX_PDEV_STATS_NUM_BE_MCS_COUNTERS]; +} htt_stats_phy_paprd_pb_tlv; + + +#define HTT_STATS_HDS_PROF_STATS_CIRCULAR_BUF_LEN 10 + +typedef struct { + htt_tlv_hdr_t tlv_hdr; + struct { + union { + A_UINT32 channel_info; + struct { + A_UINT32 bandwidth_mhz:16, + band_center_freq1:16; /* MHz units */ + }; + }; + + union { + A_UINT32 channel_config; + struct { + A_UINT32 phyMode:8, /* phyMode - WLAN_PHY_MODE enum type */ + txChainmask:8, + rxChainmask:8, + swProfile:8; + }; + }; + + A_UINT32 channelSwitchTime; + A_UINT32 calModuleTime; + A_UINT32 iniModuleTime; + A_UINT32 tpcModuleTime; + A_UINT32 miscModuleTime; + A_UINT32 ctlModuleTime; + A_UINT32 reserved; + } channelChange_stats[HTT_STATS_HDS_PROF_STATS_CIRCULAR_BUF_LEN]; + + A_UINT32 idx; /* shows how many channel changes have occurred */ +} htt_stats_hds_prof_stats_tlv; + +#define HTT_STATS_HDS_PROF_BANDWIDTH_MHZ_GET(word) \ + ((word) & 0x0000ffff) +#define HTT_STATS_HDS_PROF_BANDWIDTH_MHZ_SET(word, value) \ + ((word) |= ((value) & 0x0000ffff)) + +#define HTT_STATS_HDS_PROF_BAND_CENTER_FREQ1_GET(word) \ + (((word) & 0xffff0000) >> 16) +#define HTT_STATS_HDS_PROF_BAND_CENTER_FREQ1_SET(word, value) \ + ((word) |= (((value) << 16) & 0xffff0000)) + +#define HTT_STATS_HDS_PROF_PHY_MODE_GET(word) \ + (((word) & 0x000000ff) >> 0) +#define HTT_STATS_HDS_PROF_PHY_MODE_SET(word, value) \ + ((word) |= (((value) << 0) & 0x000000ff)) + +#define HTT_STATS_HDS_PROF_TX_CHAINMASK_GET(word) \ + (((word) & 0x0000ff00) >> 8) +#define HTT_STATS_HDS_PROF_TX_CHAINMASK_SET(word, value) \ + ((word) |= (((value) << 8) & 0x0000ff00)) + +#define HTT_STATS_HDS_PROF_RX_CHAINMASK_GET(word) \ + (((word) & 0x00ff0000) >> 16) +#define HTT_STATS_HDS_PROF_RX_CHAINMASK_SET(word, value) \ + ((word) |= (((value) << 16) & 0x00ff0000)) + +#define HTT_STATS_HDS_PROF_SW_PROFILE_GET(word) \ + (((word) & 0xff000000) >> 24) +#define HTT_STATS_HDS_PROF_SW_PROFILE_SET(word, value) \ + ((word) |= (((value) << 24) & 0xff000000)) + + typedef struct { union { A_UINT32 word32; @@ -10868,6 +11659,26 @@ typedef struct { #define HTT_ML_PEER_EXT_DETAILS_MLD_AST_INDEX_M 0x0FFFF000 #define HTT_ML_PEER_EXT_DETAILS_MLD_AST_INDEX_S 12 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_M 0x00000007 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_S 0 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_M 0x00000038 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_S 3 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_M 0x000001C0 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_S 6 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_M 0x00000E00 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_S 9 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_M 0x00007000 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_S 12 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_M 0x00038000 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_S 15 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_M 0x001C0000 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_S 18 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_M 0x00E00000 +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_S 21 +#define HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_M 0x07000000 +#define HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_S 24 + + #define HTT_ML_PEER_EXT_DETAILS_PEER_ASSOC_IPC_RECVD_GET(_var) \ (((_var) & HTT_ML_PEER_EXT_DETAILS_PEER_ASSOC_IPC_RECVD_M) >> \ HTT_ML_PEER_EXT_DETAILS_PEER_ASSOC_IPC_RECVD_S) @@ -10910,6 +11721,97 @@ typedef struct { ((_var) |= ((_val) << HTT_ML_PEER_EXT_DETAILS_MLD_AST_INDEX_S)); \ } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID0_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID1_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID2_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID3_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID4_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID5_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID6_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_TID7_TQM_LINK_ID_S)); \ + } while (0) + +#define HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_M) >> \ + HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_S) +#define HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_EXT_DETAILS_MLO_MGMT_TID_TQM_LINK_ID_S)); \ + } while (0) + typedef struct { htt_tlv_hdr_t tlv_hdr; union { @@ -10921,6 +11823,51 @@ typedef struct { }; A_UINT32 msg_dword_1; }; + union { + struct { + A_UINT32 tid0_tqm_link0_id : 3, + tid1_tqm_link0_id : 3, + tid2_tqm_link0_id : 3, + tid3_tqm_link0_id : 3, + tid4_tqm_link0_id : 3, + tid5_tqm_link0_id : 3, + tid6_tqm_link0_id : 3, + tid7_tqm_link0_id : 3, + mlo_mgmt_tid_tqm_link0_id : 3, + reserved_tqm_link0 : 5; + }; + A_UINT32 msg_dword_tqm_link0; + }; + union { + struct { + A_UINT32 tid0_tqm_link1_id : 3, + tid1_tqm_link1_id : 3, + tid2_tqm_link1_id : 3, + tid3_tqm_link1_id : 3, + tid4_tqm_link1_id : 3, + tid5_tqm_link1_id : 3, + tid6_tqm_link1_id : 3, + tid7_tqm_link1_id : 3, + mlo_mgmt_tid_tqm_link1_id : 3, + reserved_tqm_link1 : 5; + }; + A_UINT32 msg_dword_tqm_link1; + }; + union { + struct { + A_UINT32 tid0_tqm_link2_id : 3, + tid1_tqm_link2_id : 3, + tid2_tqm_link2_id : 3, + tid3_tqm_link2_id : 3, + tid4_tqm_link2_id : 3, + tid5_tqm_link2_id : 3, + tid6_tqm_link2_id : 3, + tid7_tqm_link2_id : 3, + mlo_mgmt_tid_tqm_link2_id : 3, + reserved_tqm_link2 : 5; + }; + A_UINT32 msg_dword_tqm_link2; + }; } htt_stats_ml_peer_ext_details_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_ml_peer_ext_details_tlv htt_ml_peer_ext_details_tlv; @@ -10947,11 +11894,16 @@ typedef htt_stats_ml_peer_ext_details_tlv htt_ml_peer_ext_details_tlv; #define HTT_ML_LINK_INFO_ANCHOR_LINK_S 21 #define HTT_ML_LINK_INFO_INITIALIZED_M 0x00400000 #define HTT_ML_LINK_INFO_INITIALIZED_S 22 +#define HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_M 0x00800000 +#define HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_S 23 #define HTT_ML_LINK_INFO_SW_PEER_ID_M 0x0000ffff #define HTT_ML_LINK_INFO_SW_PEER_ID_S 0 #define HTT_ML_LINK_INFO_VDEV_ID_M 0x00ff0000 #define HTT_ML_LINK_INFO_VDEV_ID_S 16 +#define HTT_STATS_ML_LINK_INFO_PS_STATE_M 0x01000000 +#define HTT_STATS_ML_LINK_INFO_PS_STATE_S 24 + #define HTT_ML_LINK_INFO_VALID_GET(_var) \ (((_var) & HTT_ML_LINK_INFO_VALID_M) >> \ @@ -11107,6 +12059,18 @@ typedef htt_stats_ml_peer_ext_details_tlv htt_ml_peer_ext_details_tlv; ((_var) |= ((_val) << HTT_ML_LINK_INFO_INITIALIZED_S)); \ } while (0) + +#define HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_GET(_var) \ + (((_var) & HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_M) >> \ + HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_S) +#define HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_LINK_INFO_BRIDGE_PEER, _val); \ + ((_var) &= ~(HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_LINK_INFO_BRIDGE_PEER_S)); \ + } while (0) + + #define HTT_ML_LINK_INFO_SW_PEER_ID_GET(_var) \ (((_var) & HTT_ML_LINK_INFO_SW_PEER_ID_M) >> \ HTT_ML_LINK_INFO_SW_PEER_ID_S) @@ -11135,6 +12099,17 @@ typedef htt_stats_ml_peer_ext_details_tlv htt_ml_peer_ext_details_tlv; ((_var) |= ((_val) << HTT_ML_LINK_INFO_VDEV_ID_S)); \ } while (0) +#define HTT_STATS_ML_LINK_INFO_PS_STATE_GET(_var) \ + (((_var) & HTT_STATS_ML_LINK_INFO_PS_STATE_M) >> \ + HTT_STATS_ML_LINK_INFO_PS_STATE_S) +#define HTT_STATS_ML_LINK_INFO_PS_STATE_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_LINK_INFO_PS_STATE, _val); \ + ((_var) &= ~(HTT_STATS_ML_LINK_INFO_PS_STATE_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_LINK_INFO_PS_STATE_S)); \ + } while (0) + + typedef struct { htt_tlv_hdr_t tlv_hdr; union { @@ -11150,7 +12125,8 @@ typedef struct { master_link : 1, anchor_link : 1, initialized : 1, - reserved : 9; + bridge_peer : 1, + reserved : 8; }; A_UINT32 msg_dword_1; }; @@ -11159,7 +12135,8 @@ typedef struct { struct { A_UINT32 sw_peer_id : 16, vdev_id : 8, - reserved1 : 8; + ps : 1, + reserved1 : 7; }; A_UINT32 msg_dword_2; }; @@ -11197,6 +12174,8 @@ typedef htt_stats_ml_link_info_details_tlv htt_ml_link_info_tlv; #define HTT_ML_PEER_DETAILS_PARTICIPATING_CHIPS_BITMAP_M 0x000000ff #define HTT_ML_PEER_DETAILS_PARTICIPATING_CHIPS_BITMAP_S 0 +#define HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_M 0x0000ff00 +#define HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_S 8 #define HTT_ML_PEER_DETAILS_NUM_LINKS_GET(_var) \ (((_var) & HTT_ML_PEER_DETAILS_NUM_LINKS_M) >> \ @@ -11382,6 +12361,17 @@ typedef htt_stats_ml_link_info_details_tlv htt_ml_link_info_tlv; ((_var) |= ((_val) << HTT_ML_PEER_DETAILS_PARTICIPATING_CHIPS_BITMAP_S)); \ } while (0) +#define HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_GET(_var) \ + (((_var) & HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_M) >> \ + HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_S) +#define HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED, _val); \ + ((_var) &= ~(HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_M)); \ + ((_var) |= ((_val) << HTT_STATS_ML_PEER_DETAILS_STATUS_REQUIRED_S)); \ + } while (0) + + typedef struct { htt_tlv_hdr_t tlv_hdr; htt_mac_addr remote_mld_mac_addr; @@ -11414,8 +12404,19 @@ typedef struct { union { struct { - A_UINT32 participating_chips_bitmap : 8, - reserved1 : 24; + A_UINT32 participating_chips_bitmap : 8, + /* status_required: + * Bitmap of status-required flags for each chip. + * Bit 0 is always the chip with the primary link. + * The remaining bits are for the other chips, + * in increasing order of chip ID, wrapping around + * to cover the chips whose IDs are smaller than the + * primary link's chip. + * Thus, bit 1 is for the chip whose ID is next after + * the primary link's chip ID, etc. + */ + status_required : 8, + reserved1 : 16; }; A_UINT32 msg_dword_2; }; @@ -11706,6 +12707,16 @@ typedef struct { * only for UL BSR TX mode. */ A_UINT32 running_ul_scheduler_for_bsrp_cnt[HTT_NUM_AC_WMM]; + /** + * Number of instances where we populated TX mode and candidate lists + * only for DL, due to skipping UL voluntarily. + */ + A_UINT32 running_dl_scheduler_due_to_skip_ul[HTT_NUM_AC_WMM]; + /** + * Number of instances where we populated TX mode and candidate lists + * only for UL, due to skipping DL voluntarily. + */ + A_UINT32 running_ul_scheduler_due_to_skip_dl[HTT_NUM_AC_WMM]; } htt_stats_pdev_sched_algo_ofdma_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_pdev_sched_algo_ofdma_stats_tlv @@ -11721,7 +12732,13 @@ typedef struct { * read/write this bitfield. * BIT [31 : 8] :- reserved */ - A_UINT32 mac_id__word; + union { + struct { + A_UINT32 mac_id: 8, + reserved: 24; + }; + A_UINT32 mac_id__word; + }; A_UINT32 basic_trigger_across_bss; A_UINT32 basic_trigger_within_bss; A_UINT32 bsr_trigger_across_bss; @@ -11884,17 +12901,37 @@ typedef struct { * BIT [ 15 : 8] :- pri20_index * BIT [ 31 : 16] :- pri20_freq in Mhz */ - A_UINT32 mac_id__pri20_idx__freq; + union { + struct { + A_UINT32 mac_id : 8; + A_UINT32 pri20_idx : 8; + A_UINT32 pri20_freq_mhz : 16; + }; + A_UINT32 mac_id__pri20_idx__freq; + }; /* BIT [ 15 : 0] :- centre_freq1 * BIT [ 31 : 16] :- centre_freq2 */ - A_UINT32 centre_freq1__freq2; + union { + struct { + A_UINT32 centre_freq1 : 16; + A_UINT32 centre_freq2 : 16; + }; + A_UINT32 centre_freq1__freq2; + }; /* BIT [ 7 : 0] :- channel_phy_mode * BIT [ 23 : 8] :- static_pattern */ - A_UINT32 phy_mode__static_pattern; + union { + struct { + A_UINT32 phy_mode : 8; + A_UINT32 static_pattern : 16; + A_UINT32 reserved : 8; + }; + A_UINT32 phy_mode__static_pattern; + }; } htt_stats_pdev_bw_mgr_stats_tlv; /* preserve old name alias for new name consistent with the tag name */ typedef htt_stats_pdev_bw_mgr_stats_tlv htt_pdev_bw_mgr_stats_tlv; diff --git a/drivers/staging/fw-api/fw/wlan_defs.h b/drivers/staging/fw-api/fw/wlan_defs.h index 2e8db1346a90..823b8ec9fedf 100755 --- a/drivers/staging/fw-api/fw/wlan_defs.h +++ b/drivers/staging/fw-api/fw/wlan_defs.h @@ -161,6 +161,18 @@ typedef enum { MODE_11BE_EHT40_2G = 32, /* For WIN */ #endif +#if defined(SUPPORT_11BN) && SUPPORT_11BN + MODE_11BN_UHR20 = 33, + MODE_11BN_UHR40 = 34, + MODE_11BN_UHR80 = 35, + MODE_11BN_UHR80_80 = 36, + MODE_11BN_UHR160 = 37, + MODE_11BN_UHR160_160 = 38, + MODE_11BN_UHR320 = 39, + MODE_11BN_UHR20_2G = 40, + MODE_11BN_UHR40_2G = 41, +#endif + /* * MODE_UNKNOWN should not be used within the host / target interface. * Thus, it is permissible for MODE_UNKNOWN to be conditionally-defined, @@ -262,6 +274,20 @@ typedef enum { ((mode) == MODE_11BE_EHT40_2G)) #endif /* SUPPORT_11BE */ +#if defined(SUPPORT_11BN) && SUPPORT_11BN +#define IS_MODE_UHR(mode) (((mode) == MODE_11BN_UHR20) || \ + ((mode) == MODE_11BN_UHR40) || \ + ((mode) == MODE_11BN_UHR80) || \ + ((mode) == MODE_11BN_UHR80_80) || \ + ((mode) == MODE_11BN_UHR160) || \ + ((mode) == MODE_11BN_UHR160_160)|| \ + ((mode) == MODE_11BN_UHR320) || \ + ((mode) == MODE_11BN_UHR20_2G) || \ + ((mode) == MODE_11BN_UHR40_2G)) +#define IS_MODE_UHR_2G(mode) (((mode) == MODE_11BN_UHR20_2G) || \ + ((mode) == MODE_11BN_UHR40_2G)) +#endif /* SUPPORT_11BN */ + #define IS_MODE_VHT_2G(mode) (((mode) == MODE_11AC_VHT20_2G) || \ ((mode) == MODE_11AC_VHT40_2G) || \ ((mode) == MODE_11AC_VHT80_2G)) diff --git a/drivers/staging/fw-api/fw/wlan_module_ids.h b/drivers/staging/fw-api/fw/wlan_module_ids.h index 981bbaef3451..6ccf2d652f95 100644 --- a/drivers/staging/fw-api/fw/wlan_module_ids.h +++ b/drivers/staging/fw-api/fw/wlan_module_ids.h @@ -196,6 +196,8 @@ typedef enum { WLAN_MODULE_C2C, /* 0x98 */ WLAN_MODULE_VBSS, /* 0x99 */ WLAN_MODULE_OPT_DATA, /* 0x9a */ + WLAN_MODULE_ASD, /* 0x9b */ + WLAN_MODULE_ENERGY_MGMT, /* 0x9c */ WLAN_MODULE_ID_MAX, WLAN_MODULE_ID_INVALID = WLAN_MODULE_ID_MAX, diff --git a/drivers/staging/fw-api/fw/wmi_services.h b/drivers/staging/fw-api/fw/wmi_services.h index b82c7bd1fc45..18f2fa6085d5 100644 --- a/drivers/staging/fw-api/fw/wmi_services.h +++ b/drivers/staging/fw-api/fw/wmi_services.h @@ -695,6 +695,32 @@ typedef enum { WMI_SERVICE_UMAC_MIGRATION_SUPPORT = 436, /* Indicates that FW supports UMAC migration */ WMI_SERVICE_STA_TWT_STATS_EXT = 437, /* FW supports additional info in TWT stats and ADD COMPLETION Event */ WMI_SERVICE_OPT_DP_DIAG_SUPPORT = 438, /* FW supports diag QDATA feature */ + WMI_SERVICE_MLO_ROAM_PARTNER_BRINGUP_FROM_HOST = 439, /* Indicates FW supports new design in which FW expects the host to bringup the partner link during roaming */ + WMI_SERVICE_CTRL_PATH_PEER_BA_STATS = 440, /* FW supports retrieving BlockAck stats through WMI_REQUEST_CTRL_PATH_PEER_STAT */ + WMI_SERVICE_CTRL_PATH_STA_DAR_STATS_SUPPORT = 441, /* FW supports DAR stats reporting for STA mode */ + WMI_SERVICE_APF_DATA_OFFLOAD_SUPPORT_ENABLED = 442, /* Indicates FW support for APFv6 handling offloads and disable QC data offloads */ + WMI_SERVICE_PER_VDEV_TWT_RESP_DISABLE_SUPPORT = 443, /* FW supports vdev level TWT responder disable */ + WMI_SERVICE_VENDOR_OUI_ACTION_V2 = 444, /* FW supports vendor OUI action version 2 */ + WMI_SERVICE_HW_BLACKLIST_CHAN_SUPPORT = 445, /* Indicates FW support for computing and sending the HW channel blacklist for the current country and applicable power mode */ + WMI_SERVICE_NDP_DFS_CHANNEL_SUPPORT = 446, /* FW supports forming NDP on DFS channels */ + WMI_SERVICE_WFD_R2 = 447, /* Indicates FW supports WiFi-Direct R2 */ + WMI_SERVICE_STA_MLO_RCFG_SUPPORT = 448, /* FW supports STA ML reconfig op */ + WMI_SERVICE_PDEV_SUSPEND_EVENT_SUPPORT = 449, /* FW supports PDEV_SUSPEND event */ + WMI_SERVICE_PCC_MODE = 450, /* Indicates FW support for PCC (P2P Connection Compatibility) Mode */ + WMI_SERVICE_TDLS_NSS_CONFIRM_SUPPORT = 451, /* FW supports confirmation to host requested TDLS NSS operation */ + + WMI_SERVICE_EM_PCIE_CONFIG_CBW_SUPPORT = 452, /* Indicates support for channel bandwidth based PCIe config adjustment */ + WMI_SERVICE_EM_PCIE_CONFIG_LPM_SUPPORT = 453, /* Indicates support for PCIe low power mode L0S/L1 */ + WMI_SERVICE_EM_DCVS_SUPPORT = 454, /* Indicates support for Dynamic clock and voltage scaling */ + WMI_SERVICE_EM_EDPS_SUPPORT = 455, /* Indicates support for Dynamic AP power save */ + WMI_SERVICE_EM_PUO_SUPPORT = 456, /* Indicates support for TWT based periodic unavailability operation. */ + WMI_SERVICE_EM_ECO_MODE_SUPPORT = 457, /* Indicates support for ECO mode config (LP BBF+ADC+SYNCT) */ + + WMI_SERVICE_11BN = 458, /* Indicates FW supports 802.11bn */ + WMI_SERVICE_HOST_AWARE_POWERSAVE = 459, /* FW supports indicating the powerstate of FW to host */ + WMI_SERVICE_PDEV_DIV_STATES_REPORT = 460, /* FW supports reporting antenna diversity states */ + WMI_SERVICE_EAPOL_OVER_RAW = 461, /* FW supports sending EAPOL frames in raw mode even when the vdev is brought up in nwifi/ethernet mode */ + WMI_SERVICE_MLO_SAP_LINK_REMOVAL_SUPPORT = 462, /* Indicates FW supports MLO SAP link removal operation */ WMI_MAX_EXT2_SERVICE diff --git a/drivers/staging/fw-api/fw/wmi_tlv_defs.h b/drivers/staging/fw-api/fw/wmi_tlv_defs.h index e8c306dbcb7f..54ea736a2375 100644 --- a/drivers/staging/fw-api/fw/wmi_tlv_defs.h +++ b/drivers/staging/fw-api/fw/wmi_tlv_defs.h @@ -1476,6 +1476,29 @@ typedef enum { WMITLV_TAG_STRUC_wmi_vdev_vbss_peer_sn_info, WMITLV_TAG_STRUC_wmi_vdev_vbss_config_event_fixed_param, WMITLV_TAG_STRUC_wmi_stats_ext_event_vdev_ext2_t, + WMITLV_TAG_STRUC_wmi_ndp_set_latency_tput_fixed_param, + WMITLV_TAG_STRUC_wmi_roam_partner_link_param, + WMITLV_TAG_STRUC_wmi_mlo_link_ttlm_complete_fixed_param, + WMITLV_TAG_STRUC_wmi_ctrl_path_sta_dar_stats_struct, + WMITLV_TAG_STRUC_wmi_bpf_set_supported_offload_bitmap_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_hw_blacklist_chan_fixed_param, + WMITLV_TAG_STRUC_wmi_hw_blacklist_chan_data, + WMITLV_TAG_STRUC_wmi_pdev_suspend_event_fixed_param, + WMITLV_TAG_STRUC_wmi_bpf_set_apf_mode_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_peer_assoc_operating_mode_params, + WMITLV_TAG_STRUC_wmi_recv_bcn_stats, + WMITLV_TAG_STRUC_wmi_vdev_vbss_peer_dyn_info, + WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_config_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_lpm_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_energy_mgmt_dcvs_config_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_energy_mgmt_edps_config_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_energy_mgmt_puo_config_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_energy_mgmt_eco_mode_config_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_peer_assoc_mgmt_mpduq_params, + WMITLV_TAG_STRUC_wmi_peer_assoc_mgmt_msduq_params, + WMITLV_TAG_STRUC_wmi_peer_assoc_hol_mdsuq_params, + WMITLV_TAG_STRUC_wmi_peer_tid_rate_custom_cmd_fixed_param, + WMITLV_TAG_STRUC_wmi_co_located_chan_info, } WMITLV_TAG_ID; /* * IMPORTANT: Please add _ALL_ WMI Commands Here. @@ -2037,6 +2060,17 @@ typedef enum { OP(WMI_MLO_LINK_RECONFIG_COMPLETE_CMDID) \ OP(WMI_SAWF_EZMESH_HOP_COUNT_CMDID) \ OP(WMI_VDEV_VBSS_CONFIG_CMDID) \ + OP(WMI_NDP_SET_LATENCY_TPUT_CMDID) \ + OP(WMI_MLO_LINK_TTLM_COMPLETE_CMDID) \ + OP(WMI_BPF_SET_SUPPORTED_OFFLOAD_BITMAP_CMDID) \ + OP(WMI_BPF_SET_APF_MODE_CMDID) \ + OP(WMI_ENERGY_MGMT_PCIE_CONFIG_CMDID) \ + OP(WMI_ENERGY_MGMT_PCIE_LPM_CMDID) \ + OP(WMI_ENERGY_MGMT_DCVS_CONFIG_CMDID) \ + OP(WMI_ENERGY_MGMT_EDPS_CONFIG_CMDID) \ + OP(WMI_ENERGY_MGMT_PUO_CONFIG_CMDID) \ + OP(WMI_ENERGY_MGMT_ECO_MODE_CONFIG_CMDID) \ + OP(WMI_PEER_TID_RATE_CUSTOM_CMDID) \ /* add new CMD_LIST elements above this line */ @@ -2372,6 +2406,8 @@ typedef enum { OP(WMI_PDEV_WIFI_RADAR_CAPABILITIES_EVENTID) \ OP(WMI_VDEV_VBSS_CONFIG_EVENTID) \ OP(WMI_OPT_DP_DIAG_EVENTID) \ + OP(WMI_HW_BLACKLIST_CHAN_EVENTID) \ + OP(WMI_PDEV_SUSPEND_EVENTID) \ /* add new EVT_LIST elements above this line */ @@ -2867,7 +2903,11 @@ WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_mlo_params, mlo_params, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_eht_rate_set, peer_eht_rates, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_mlo_partner_link_params, partner_link_params, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_tid_to_link_map, peer_tid_to_link_map, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_tid_to_link_map, peer_tid_to_link_map, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_operating_mode_params, operating_mode_params, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_mgmt_mpduq_params, mgmt_mpduq_params, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_mgmt_msduq_params, mgmt_msduq_params, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_assoc_hol_mdsuq_params, hol_mdsuq_params, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_PEER_ASSOC_CMDID); @@ -3611,7 +3651,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_DELETE_CMDID); /* Vdev up Cmd */ #define WMITLV_TABLE_WMI_VDEV_UP_CMDID(id,op,buf,len) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_up_cmd_fixed_param, wmi_vdev_up_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_up_cmd_fixed_param, wmi_vdev_up_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_co_located_chan_info, co_located_chan_info, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_UP_CMDID); @@ -3643,7 +3684,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_UPDATE_MAC_ADDR_CMDID); #define WMITLV_TABLE_WMI_VDEV_VBSS_CONFIG_CMDID(id,op,buf,len) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_vbss_config_cmd_fixed_param, wmi_vdev_vbss_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)\ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_pn_info, vbss_peer_pn_info, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_sn_info, vbss_peer_sn_info, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_sn_info, vbss_peer_sn_info, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_dyn_info, vbss_peer_dyn_info, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_VBSS_CONFIG_CMDID); /* Pdev suspend Cmd */ @@ -4099,6 +4141,11 @@ WMITLV_CREATE_PARAM_STRUC(WMI_NDP_END_REQ_CMDID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndp_cmd_param, wmi_ndp_cmd_param, fixed_param, WMITLV_SIZE_FIX) WMITLV_CREATE_PARAM_STRUC(WMI_NDP_CMDID); +/* NDP Set Latency Tput Cmd */ +#define WMITLV_TABLE_WMI_NDP_SET_LATENCY_TPUT_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndp_set_latency_tput_fixed_param, wmi_ndp_set_latency_tput_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_NDP_SET_LATENCY_TPUT_CMDID); + /* RCPI Info Request Cmd */ #define WMITLV_TABLE_WMI_REQUEST_RCPI_CMDID(id,op,buf,len) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_request_rcpi_cmd_fixed_param, wmi_request_rcpi_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) @@ -5384,6 +5431,11 @@ WMITLV_CREATE_PARAM_STRUC(WMI_MLO_LINK_RECONFIG_CMDID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_mlo_link_reconfig_complete_fixed_param, wmi_mlo_link_reconfig_complete_fixed_param, fixed_param, WMITLV_SIZE_FIX) WMITLV_CREATE_PARAM_STRUC(WMI_MLO_LINK_RECONFIG_COMPLETE_CMDID); +/** WMI cmd to notify fw completion of link TTLM negotiation */ +#define WMITLV_TABLE_WMI_MLO_LINK_TTLM_COMPLETE_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_mlo_link_ttlm_complete_fixed_param, wmi_mlo_link_ttlm_complete_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_MLO_LINK_TTLM_COMPLETE_CMDID); + /* Mcast ipv4 address filter list cmd */ #define WMITLV_TABLE_WMI_VDEV_IGMP_OFFLOAD_CMDID(id,op,buf,len) \ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_igmp_offload_fixed_param, wmi_igmp_offload_fixed_param, fixed_param, WMITLV_SIZE_FIX) \ @@ -5745,6 +5797,50 @@ WMITLV_CREATE_PARAM_STRUC(WMI_GET_SCAN_CACHE_RESULT_CMDID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_sawf_ezmesh_hop_count_cmd_fixed_param, wmi_sawf_ezmesh_hop_count_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) WMITLV_CREATE_PARAM_STRUC(WMI_SAWF_EZMESH_HOP_COUNT_CMDID); +#define WMITLV_TABLE_WMI_BPF_SET_SUPPORTED_OFFLOAD_BITMAP_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_bpf_set_supported_offload_bitmap_cmd_fixed_param, wmi_bpf_set_supported_offload_bitmap_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_BPF_SET_SUPPORTED_OFFLOAD_BITMAP_CMDID); + +#define WMITLV_TABLE_WMI_BPF_SET_APF_MODE_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_bpf_set_apf_mode_cmd_fixed_param, wmi_bpf_set_apf_mode_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_BPF_SET_APF_MODE_CMDID); + +/* WMI cmd used to control PCIe config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_PCIE_CONFIG_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_config_cmd_fixed_param, wmi_energy_mgmt_pcie_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_PCIE_CONFIG_CMDID); + +/* WMI cmd used for PCIe LPM config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_PCIE_LPM_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_lpm_cmd_fixed_param, wmi_energy_mgmt_pcie_lpm_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_PCIE_LPM_CMDID); + +/* WMI cmd used to control DCVS config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_DCVS_CONFIG_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_dcvs_config_cmd_fixed_param, wmi_energy_mgmt_dcvs_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_DCVS_CONFIG_CMDID); + +/* WMI cmd used to control Dynamic AP Power Save config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_EDPS_CONFIG_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_edps_config_cmd_fixed_param, wmi_energy_mgmt_edps_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_EDPS_CONFIG_CMDID); + +/* WMI cmd used to control Scheduled AP Power Save config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_PUO_CONFIG_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_puo_config_cmd_fixed_param, wmi_energy_mgmt_puo_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_PUO_CONFIG_CMDID); + +/* WMI cmd used to control Un-scheduled AP Power Save config */ +#define WMITLV_TABLE_WMI_ENERGY_MGMT_ECO_MODE_CONFIG_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_energy_mgmt_eco_mode_config_cmd_fixed_param, wmi_energy_mgmt_eco_mode_config_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_ENERGY_MGMT_ECO_MODE_CONFIG_CMDID); + +/* peer tid rate customization cmd */ +#define WMITLV_TABLE_WMI_PEER_TID_RATE_CUSTOM_CMDID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_tid_rate_custom_cmd_fixed_param, wmi_peer_tid_rate_custom_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, rate_code, WMITLV_SIZE_VAR) +WMITLV_CREATE_PARAM_STRUC(WMI_PEER_TID_RATE_CUSTOM_CMDID); + /************************** TLV definitions of WMI events *******************************/ @@ -6092,7 +6188,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_AGGR_STATE_TRIG_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, deauth_disassoc_frame, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_hw_mode_transition_event_fixed_param, hw_mode_transition_fixed_param, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_set_hw_mode_response_vdev_mac_entry, wmi_pdev_set_hw_mode_response_vdev_mac_mapping, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_bss_info_param, bss_info_param, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_bss_info_param, bss_info_param, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_partner_link_param, partner_link_param, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_EVENTID); /* Roam Synch Event */ @@ -6365,7 +6462,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_HOST_SWFDA_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pmf_bcn_protect_stats, pmf_bcn_protect_stats, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_extd_stats, vdev_extd_stats, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_extd_stats, pdev_extd_stats, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_telemetry_stats, pdev_telemetry_stats, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_telemetry_stats, pdev_telemetry_stats, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_recv_bcn_stats, recv_bcn_stats, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_UPDATE_STATS_EVENTID); /* Update PN response Event */ @@ -6801,6 +6899,11 @@ WMITLV_CREATE_PARAM_STRUC(WMI_MDNS_STATS_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_resume_event_fixed_param, wmi_pdev_resume_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_RESUME_EVENTID); +/* pdev suspend event */ +#define WMITLV_TABLE_WMI_PDEV_SUSPEND_EVENTID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_suspend_event_fixed_param, wmi_pdev_suspend_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) +WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_SUSPEND_EVENTID); + /* SAP Authentication offload event */ #define WMITLV_TABLE_WMI_SAP_OFL_ADD_STA_EVENTID(id,op,buf,len) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_sap_ofl_add_sta_event_fixed_param, wmi_sap_ofl_add_sta_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \ @@ -6954,7 +7057,9 @@ WMITLV_CREATE_PARAM_STRUC(WMI_REG_CHAN_LIST_CC_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_regulatory_chan_priority_struct, reg_chan_priority, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_regulatory_fcc_rule_struct, reg_fcc_rule, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_reg_chan_list_cc_ext_additional_params, reg_more_data, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_regulatory_rule_meta_data, reg_meta_data, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_regulatory_rule_meta_data, reg_meta_data, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_hw_blacklist_chan_fixed_param, hw_blacklist_chan_fixed_param, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_hw_blacklist_chan_data, hw_blacklist_chan_data, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_REG_CHAN_LIST_CC_EXT_EVENTID); /* WMI AFC info event */ @@ -6964,9 +7069,17 @@ WMITLV_CREATE_PARAM_STRUC(WMI_REG_CHAN_LIST_CC_EXT_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_afc_power_event_param, wmi_afc_power_event_param, afc_power_event_param, WMITLV_SIZE_FIX)\ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_6g_afc_frequency_info, freq_info_array, WMITLV_SIZE_VAR)\ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_6g_afc_channel_info, channel_info_array, WMITLV_SIZE_VAR)\ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_afc_chan_eirp_power_info, chan_eirp_power_info_array, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_afc_chan_eirp_power_info, chan_eirp_power_info_array, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_hw_blacklist_chan_fixed_param, hw_blacklist_chan_fixed_param, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_hw_blacklist_chan_data, hw_blacklist_chan_data, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_AFC_EVENTID); +/* HW blacklist channels for the current country code */ +#define WMITLV_TABLE_WMI_HW_BLACKLIST_CHAN_EVENTID(id,op,buf,len) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_hw_blacklist_chan_fixed_param, wmi_hw_blacklist_chan_fixed_param, hw_blacklist_chan_fixed_param, WMITLV_SIZE_FIX) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_hw_blacklist_chan_data, hw_blacklist_chan_data, WMITLV_SIZE_VAR) +WMITLV_CREATE_PARAM_STRUC(WMI_HW_BLACKLIST_CHAN_EVENTID); + /* Indicate LPI AP detect or not to Host */ #define WMITLV_TABLE_WMI_C2C_DETECT_EVENTID(id,op,buf,len) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_c2c_detect_event_fixed_param, wmi_c2c_detect_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) @@ -7257,7 +7370,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_PEER_STATS_INFO_EVENTID); WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_vdev_bcn_tx_stats_struct, ctrl_path_vdev_bcn_tx_stats, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_pdev_bcn_tx_stats_struct, ctrl_path_pdev_bcn_tx_stats, WMITLV_SIZE_VAR) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_pdev_conn_stats_struct, ctrl_path_pdev_conn_stats, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_ml_rcfg_stats_struct, ctrl_path_ml_rcfg_stats, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_ml_rcfg_stats_struct, ctrl_path_ml_rcfg_stats, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ctrl_path_sta_dar_stats_struct, ctrl_path_sta_dar_stats, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_CTRL_PATH_STATS_EVENTID); /* @@ -7775,7 +7889,8 @@ WMITLV_CREATE_PARAM_STRUC(WMI_VENDOR_PEER_EVENTID); #define WMITLV_TABLE_WMI_VDEV_VBSS_CONFIG_EVENTID(id,op,buf,len) \ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_vbss_config_event_fixed_param, wmi_vdev_vbss_config_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)\ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_pn_info, vbss_peer_pn_info, WMITLV_SIZE_VAR) \ - WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_sn_info, vbss_peer_sn_info, WMITLV_SIZE_VAR) + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_sn_info, vbss_peer_sn_info, WMITLV_SIZE_VAR) \ + WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_vbss_peer_dyn_info, vbss_peer_dyn_info, WMITLV_SIZE_VAR) WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_VBSS_CONFIG_EVENTID); /* link switch event */ diff --git a/drivers/staging/fw-api/fw/wmi_unified.h b/drivers/staging/fw-api/fw/wmi_unified.h index 70eaf1826cf7..49ad445c6dbf 100644 --- a/drivers/staging/fw-api/fw/wmi_unified.h +++ b/drivers/staging/fw-api/fw/wmi_unified.h @@ -369,7 +369,8 @@ typedef enum { WMI_GRP_QUIET_OFL, /* 0x4a Quiet offloads */ WMI_GRP_ODD, /* 0x4b ODD */ WMI_GRP_TDMA, /* 0x4c TDMA */ - WMI_GRP_MANUAL_UL_TRIG /* 0x4d Manual UL OFDMA Trigger */ + WMI_GRP_MANUAL_UL_TRIG, /* 0x4d Manual UL OFDMA Trigger */ + WMI_GRP_ENERGY_MGMT, /* 0x4e energy management */ } WMI_GRP_ID; #define WMI_CMD_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) @@ -851,6 +852,9 @@ typedef enum { /* WMI command to setup reorder queue for multiple TIDs */ WMI_PEER_MULTIPLE_REORDER_QUEUE_SETUP_CMDID, + /** Customize MCS range for specific tid */ + WMI_PEER_TID_RATE_CUSTOM_CMDID, + /* beacon/management specific commands */ /** transmit beacon by reference . used for transmitting beacon on low latency interface like pcie */ @@ -1579,6 +1583,8 @@ typedef enum { WMI_BPF_SET_VDEV_ENABLE_CMDID, WMI_BPF_SET_VDEV_WORK_MEMORY_CMDID, WMI_BPF_GET_VDEV_WORK_MEMORY_CMDID, + WMI_BPF_SET_SUPPORTED_OFFLOAD_BITMAP_CMDID, + WMI_BPF_SET_APF_MODE_CMDID, /** WMI commands related to monitor mode. */ WMI_MNT_FILTER_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_MONITOR), @@ -1601,6 +1607,7 @@ typedef enum { WMI_NDP_RESPONDER_REQ_CMDID, WMI_NDP_END_REQ_CMDID, WMI_NDP_CMDID, + WMI_NDP_SET_LATENCY_TPUT_CMDID, /** WMI commands related to HW data filtering **/ WMI_HW_DATA_FILTER_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_HW_DATA_FILTER), @@ -1702,6 +1709,8 @@ typedef enum { WMI_MLO_LINK_RECONFIG_CMDID, /** WMI cmd to notify fw completion of link reconfig */ WMI_MLO_LINK_RECONFIG_COMPLETE_CMDID, + /** WMI cmd to notify fw completion of negotiated TID to LINK map */ + WMI_MLO_LINK_TTLM_COMPLETE_CMDID, /** WMI commands specific to Service Aware WiFi (SAWF) */ /** configure or reconfigure the parameters for a service class */ @@ -1723,6 +1732,21 @@ typedef enum { /** WMI Command to set Manual MU UL OFDMA trigger parameters */ WMI_VDEV_SET_ULOFDMA_MANUAL_MU_TRIG_CMDID, + + + /** WMI commands specific to Energy Management **/ + /** WMI cmd used to control PCIe config */ + WMI_ENERGY_MGMT_PCIE_CONFIG_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_ENERGY_MGMT), + /** WMI cmd used to control PCIe LPM */ + WMI_ENERGY_MGMT_PCIE_LPM_CMDID, + /** WMI cmd used to control Clock and Voltage config */ + WMI_ENERGY_MGMT_DCVS_CONFIG_CMDID, + /** WMI cmd used to control AP Dynamic Power Save feature */ + WMI_ENERGY_MGMT_EDPS_CONFIG_CMDID, + /** WMI cmd used to control periodic unavailability operation */ + WMI_ENERGY_MGMT_PUO_CONFIG_CMDID, + /** WMI cmd used to control ECO mode config */ + WMI_ENERGY_MGMT_ECO_MODE_CONFIG_CMDID, } WMI_CMD_ID; typedef enum { @@ -2163,6 +2187,7 @@ typedef enum { /* send pdev resume event to host after pdev resume. */ WMI_PDEV_RESUME_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SUSPEND), + WMI_PDEV_SUSPEND_EVENTID, /** WOW wake up host event.generated in response to WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID. will cary wake reason */ @@ -2529,6 +2554,8 @@ typedef enum { WMI_AFC_EVENTID, WMI_REG_CHAN_LIST_CC_EXT2_EVENTID, /* DEPRECATED */ WMI_C2C_DETECT_EVENTID, + /* WMI event to send the HW channel blacklist during the CTL blob update */ + WMI_HW_BLACKLIST_CHAN_EVENTID, /** Events for TWT(Target Wake Time) of STA and AP */ WMI_TWT_ENABLE_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_TWT), @@ -2588,6 +2615,9 @@ typedef enum { WMI_MLO_VDEV_LINK_INFO_EVENTID, /** request host to do T2LM neg to the un-disabled link */ WMI_MLO_LINK_DISABLE_REQUEST_EVENTID, + /* alias */ + WMI_MLO_LINK_TTLM_REQUEST_EVENTID = + WMI_MLO_LINK_DISABLE_REQUEST_EVENTID, /** request host to switch to new link for specified vdev */ WMI_MLO_LINK_SWITCH_REQUEST_EVENTID, /** Response event for WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_CMDID */ @@ -3563,6 +3593,21 @@ typedef struct { #define WMI_TARGET_CAP_MPDU_STATS_PER_TX_NSS_SUPPORT_SET(target_cap_flags, value)\ WMI_SET_BITS(target_cap_flags, 16, 1, value) +#define WMI_TARGET_CAP_MAX_ML_STA_BSS_NUM_GET(target_cap_flags) \ + WMI_GET_BITS(target_cap_flags, 17, 3) +#define WMI_TARGET_CAP_MAX_ML_STA_BSS_NUM_SET(target_cap_flags, value) \ + WMI_SET_BITS(target_cap_flags, 17, 3, value) + +#define WMI_TARGET_CAP_MAX_ML_SAP_BSS_NUM_GET(target_cap_flags) \ + WMI_GET_BITS(target_cap_flags, 20, 3) +#define WMI_TARGET_CAP_MAX_ML_SAP_BSS_NUM_SET(target_cap_flags, value) \ + WMI_SET_BITS(target_cap_flags, 20, 3, value) + +#define WMI_TARGET_CAP_TOTAL_ML_LINKS_NUM_GET(target_cap_flags) \ + WMI_GET_BITS(target_cap_flags, 23, 3) +#define WMI_TARGET_CAP_TOTAL_ML_LINKS_NUM_SET(target_cap_flags, value) \ + WMI_SET_BITS(target_cap_flags, 23, 3, value) + /* * wmi_htt_msdu_idx_to_htt_msdu_qtype GET/SET APIs @@ -3762,7 +3807,10 @@ typedef struct { * Bit 14 - Support for ML monitor mode * Bit 15 - Support for Qdata Tx LCE filter installation * Bit 16 - Support for MPDU stats per tx Nss capability - * Bits 31:17 - Reserved + * Bits 19:17 - max number of ML STA BSS supported, range [0-7] + * Bits 22:20 - max number of ML SAP BSS supported, range [0-7] + * Bits 25:23 - total number of ML links supported, range [0-7] + * Bits 31:26 - Reserved */ A_UINT32 target_cap_flags; @@ -3860,10 +3908,18 @@ typedef struct { * WMI_HAL_REG_CAPABILITIES_EXT2 hal_reg_caps[]; * wmi_nan_capabilities nan_cap; * WMI_SCAN_RADIO_CAPABILITIES_EXT2 wmi_scan_radio_caps[]; + * wmi_twt_caps_params twt_caps[]; * wmi_htt_msdu_idx_to_htt_msdu_qtype htt_msdu_idx_to_qtype_map[]; * wmi_dbs_or_sbs_cap_ext dbs_or_sbs_cap_ext; + * wmi_cust_bdf_version_capabilities cust_bdf_version_capabilities[]; + * wmi_sw_cal_ver_cap sw_cal_ver_cap[]; * A_INT32 hw_tx_power_signed[WMI_HW_TX_POWER_CAPS_MAX]; + * WMI_COEX_FIX_CHANNEL_CAPABILITIES coex_fix_channel_caps[]; * wmi_aux_dev_capabilities aux_dev_caps[]; + * wmi_enhanced_aoa_caps_param aoa_caps_param[]; + * wmi_enhanced_aoa_per_band_caps_param + * aoa_per_band_caps_param[]; + * wmi_sar_flag_tlv_param sar_flags[]; * WMI_POWER_BOOST_CAPABILITIES power_boost_capabilities[]; * WMI_RSSI_ACCURACY_IMPROVEMENT_CAPABILITIES * rssi_accuracy_improvement_capabilities[]; @@ -4833,8 +4889,13 @@ typedef struct { * 11 -> reserved * Refer to the below WMI_RSRC_CFG_FLAGS2_OPTIMIZE_POWER_GET/SET * macros. + * Bit 25 - enable recv_bcn_stats feature + * 0 -> disable the feature + * 1 -> enable the feature + * Refer to below WMI_RSRC_CFG_FLAGS2_RECV_BCN_STATS_ENABLED_GET/SET + * macros. * - * Bits 31:25 - Reserved + * Bits 31:26 - Reserved */ A_UINT32 flags2; /** @brief host_service_flags - can be used by Host to indicate @@ -4958,7 +5019,38 @@ typedef struct { * So FW will start refilling the buffers. * Refer to the below definitions of WMI_RSRC_CFG_HOST_SERVICE_FLAG * OPT_DP_CTRL_REPLENISH_REFILL_RX_BUFFER_SUPPORT_GET and _SET macros. - * Bits 31:18 - Reserved + * Bit 18 + * This bit will be set by host to inform FW that VBSS feature is + * enabled. + * Bit 19 + * This bit will set by host to inform FW that the bypass approach + * for HLOS TID OVERRIDE feature not working needs to be supported, + * So FW will start handling the PPE2TCL enqueued packets with + * flow_override set. + * Refer to the below definitions of WMI_RSRC_CFG_HOST_SERVICE_FLAG + * OPT_DP_ENABLE_BYPASS_FOR_HLOS_TID_OVERRIDE_GET and _SET macros. + * Bit 20 + * This bit will set by host to inform FW that action OUI v2 is + * enabled by both host configuration and FW capability. + * Refer to the below definitions of + * WMI_RSRC_CFG_HOST_SERVICE_FLAG_ACTION_OUI_V2_GET and SET. + * Bit 21 + * This bit will be set by host to inform FW that HW blacklist + * channel is supported in host. Based on this flag, FW will do + * CTL generation for all the IEEE channels allowed in the VLP + * and SP power for the current county and update host in the + * below WMI Events: + * WMI_REG_CHAN_LIST_CC_EXT_EVENTID, WMI_AFC_EVENTID, and + * WMI_HW_BLACKLIST_CHAN_EVENTID + * Bit 22 + * This bit will be set by host to inform FW that AFC handling + * is supported in the default cc event id. Based on this flag, + * FW will send the AFC event in the default cc event id if the + * country supports SP regulatory rules. + * Refer to the below defintions of WMI_RSRC_CFG_HOST_SERVICE_FLAG + * AFC_TRIGGER_ON_DEFAULT_CC_EVENT_GET and _SET macros. + * + * Bits 31:23 - Reserved */ A_UINT32 host_service_flags; @@ -5075,7 +5167,35 @@ typedef struct { * BIT 6 : 31 Reserved */ A_UINT32 c2c_int_type_config; + + /** + * @brief apf_data_ofld_enable + * BIT 0 -> enable/disable offload support in apf + * @detail: This flag will indicate during init time + * based on the vendor img version if APF is supporting + * any offloads. + * BIT 1 : 31 Reserved + */ + union { + A_UINT32 apf_data_ofload_enable__word; + struct { + A_UINT32 + apf_data_ofld_enable: 1, + reserved: 31; + }; + }; + /** + * @brief HAPS flags setting for power save config + * bit 0 : Enable disable haps feature + * bit 1 : Sync and update qtime cnss timestamp + * BIT 2-31: Reserved + */ + A_UINT32 dp_haps_config; } wmi_resource_config; +#define WMI_RSRC_CFG_APF_DATA_OFLD_ENABLE_GET(word32) \ + WMI_GET_BITS(word32, 0, 1) +#define WMI_RSRC_CFG_APF_DATA_OFLD_ENABLE_SET(word32, value) \ + WMI_SET_BITS(word32, 0, 1, value) #define WMI_MSDU_FLOW_AST_ENABLE_GET(msdu_flow_config0, ast_x) \ (((ast_x) == 0) ? 1 : ((msdu_flow_config0) & (1 << ((ast_x) - 1)))) @@ -5369,6 +5489,11 @@ typedef struct { #define WMI_RSRC_CFG_FLAGS2_OPTIMIZE_POWER_SET(flags2, value) \ WMI_SET_BITS(flags2, 23, 2, value) +#define WMI_RSRC_CFG_FLAGS2_RECV_BCN_STATS_ENABLED_GET(flags2) \ + WMI_GET_BITS(flags2, 25, 1) +#define WMI_RSRC_CFG_FLAGS2_RECV_BCN_STATS_ENABLED_SET(flags2, value) \ + WMI_SET_BITS(flags2, 25, 1, value) + #define WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT_GET(host_service_flags) \ WMI_GET_BITS(host_service_flags, 0, 1) @@ -5476,6 +5601,35 @@ typedef struct { #define WMI_RSRC_CFG_HOST_SERVICE_FLAG_VBSS_ENABLED_SET(host_service_flags, val) \ WMI_SET_BITS(host_service_flags, 18, 1, val) +/* + * This bit is to inform that we need to enable the bypass approach + * (for HLOS TID override feature not working in the target) + * to handle flowq creation from FW when flow override is set and + * frames are recvd from PPE2TCL. + * */ +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_OPT_DP_ENABLE_BYPASS_FOR_HLOS_TID_OVERRIDE_GET(host_service_flags) \ + WMI_GET_BITS(host_service_flags, 19, 1) +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_OPT_DP_ENABLE_BYPASS_FOR_HLOS_TID_OVERRIDE_SET(host_service_flags, val) \ + WMI_SET_BITS(host_service_flags, 19, 1, val) + +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_ACTION_OUI_V2_GET(host_service_flags) \ + WMI_GET_BITS(host_service_flags, 20, 1) +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_ACTION_OUI_V2_SET(host_service_flags, val) \ + WMI_SET_BITS(host_service_flags, 20, 1, val) + +/* This bit is used to inform FW to send HW Blacklist channels to host */ +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_HOST_SUPPORT_HW_BLACKLIST_CHANNEL_SUPPORT_GET(host_service_flags) \ + WMI_GET_BITS(host_service_flags, 21, 1) +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_HOST_SUPPORT_HW_BLACKLIST_CHANNEL_SUPPORT_SET(host_service_flags, val) \ + WMI_SET_BITS(host_service_flags, 21, 1, val) + +/* This bit is used to inform FW to send AFC event ID in default CC event ID */ +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_AFC_TRIGGER_ON_DEFAULT_CC_EVENT_GET(host_service_flags) \ + WMI_GET_BITS(host_service_flags, 22, 1) +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_AFC_TRIGGER_ON_DEFAULT_CC_EVENT_SET(host_service_flags, val) \ + WMI_SET_BITS(host_service_flags, 22, 1, val) + + #define WMI_RSRC_CFG_CARRIER_CFG_CHARTER_ENABLE_GET(carrier_config) \ WMI_GET_BITS(carrier_config, 0, 1) #define WMI_RSRC_CFG_CARRIER_CFG_CHARTER_ENABLE_SET(carrier_config, val) \ @@ -5549,6 +5703,7 @@ typedef enum { WMI_VENDOR1_REQ1_VERSION_4_10 = 6, WMI_VENDOR1_REQ1_VERSION_4_20 = 7, WMI_VENDOR1_REQ1_VERSION_4_40 = 8, + WMI_VENDOR1_REQ1_VERSION_4_50 = 9, } WMI_VENDOR1_REQ1_VERSION; typedef enum { @@ -5556,6 +5711,7 @@ typedef enum { WMI_VENDOR1_REQ2_VERSION_3_01 = 1, WMI_VENDOR1_REQ2_VERSION_3_20 = 2, WMI_VENDOR1_REQ2_VERSION_3_50 = 3, + WMI_VENDOR1_REQ2_VERSION_3_61 = 4, } WMI_VENDOR1_REQ2_VERSION; typedef enum { @@ -6285,16 +6441,18 @@ typedef struct { #define WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT 0x40000 #define WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ 0x80000 #define WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ 0x100000 -/** pause home channel when scan channel is same as home channel */ -#define WMI_SCAN_FLAG_PAUSE_HOME_CHANNEL 0x200000 -/** - * report CCA busy for each possible 20Mhz subbands of the wideband scan channel - */ -#define WMI_SCAN_FLAG_REPORT_CCA_BUSY_FOREACH_20MHZ 0x400000 - /** for adaptive scan mode using 3 bits (21 - 23 bits) */ #define WMI_SCAN_DWELL_MODE_MASK 0x00E00000 #define WMI_SCAN_DWELL_MODE_SHIFT 21 +/** pause home channel when scan channel is same as home channel (bit 24) */ +#define WMI_SCAN_FLAG_PAUSE_HOME_CHANNEL 0x01000000 +/** + * report CCA busy for each possible 20MHz subband of the wideband scan channel + * (bit 25) + */ +#define WMI_SCAN_FLAG_REPORT_CCA_BUSY_FOREACH_20MHZ 0x02000000 +/* Premium scan to receive higher MCS packets in scan channel */ +#define WMI_SCAN_FLAG_PREMIUM_SCAN 0x04000000 typedef enum { WMI_SCAN_DWELL_MODE_DEFAULT = 0, @@ -6313,7 +6471,11 @@ typedef enum { #define WMI_SCAN_GET_DWELL_MODE(flag) \ (((flag) & WMI_SCAN_DWELL_MODE_MASK) >> WMI_SCAN_DWELL_MODE_SHIFT) -/** WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */ +/** + * WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK + * This bitmask is used to set/get values of req_type variable in + * wmi_stop_scan_cmd_fixed_param. + */ #define WMI_SCAN_CLASS_MASK 0xFF000000 /* @@ -9943,6 +10105,9 @@ typedef enum { /* To enable/disable DFS radar detection for scan radio */ WMI_PDEV_PARAM_ENABLE_SCAN_RADIO_DFS, + + /* configure CCE rules based on ethertype match */ + WMI_PDEV_PARAM_CONFIG_CUSTOM_CCE_RULE, } WMI_PDEV_PARAM; #define WMI_PDEV_ONLY_BSR_TRIG_IS_ENABLED(trig_type) WMI_GET_BITS(trig_type, 0, 1) @@ -10027,6 +10192,8 @@ typedef enum { #define WMI_PDEV_UPPER_CAP_DL_DIR_SET(_value, value) WMI_SET_BITS(_value, 18, 1, value) #define WMI_PDEV_UPPER_CAP_UL_DIR_GET(value) WMI_GET_BITS(value, 19, 1) #define WMI_PDEV_UPPER_CAP_UL_DIR_SET(_value, value) WMI_SET_BITS(_value, 19, 1, value) +#define WMI_PDEV_UPPER_CAP_DIR_GET(value) WMI_GET_BITS(value, 18, 1) +#define WMI_PDEV_UPPER_CAP_DIR_SET(_value, value) WMI_SET_BITS(_value, 18, 1, value) #define WMI_PDEV_RATE_DROP_NUM_MCS_GET(value) WMI_GET_BITS(value, 0, 8) #define WMI_PDEV_RATE_DROP_NUM_MCS_SET(_value, value) WMI_SET_BITS(_value, 0, 8, value) @@ -10600,6 +10767,16 @@ typedef struct { */ } wmi_pdev_tpc_config_event_fixed_param; + +typedef enum { + WMI_ASD_PRIMARY_ANT = 0, + WMI_ASD_DIVERSITY_ANT = 1, + WMI_ASD_THIRD_ANT = 2, + WMI_ASD_FOURTH_ANT = 3, + + WMI_ASD_MAX_ANTTYPE = 4 +} WMI_ASD_ANT_TYPE; + typedef struct { /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_div_rssi_antid_event_fixed_param */ A_UINT32 tlv_header; @@ -10613,6 +10790,17 @@ typedef struct { wmi_mac_addr macaddr; /* EVM value for stream0 and stream1 20Mhz, dB units */ A_INT32 chain_evm[WMI_MAX_CHAINS]; + /** num_antennas_valid: + * how many elements in the WMI_ASD_MAX_ANTTYPE arrays below + * contain valid info + */ + A_UINT32 num_antennas_valid; + /** switch count on each antenna attached to each chain */ + A_UINT32 ant_cnt[WMI_MAX_CHAINS][WMI_ASD_MAX_ANTTYPE]; + /** stay duration on each antenna attached to each chain (units: ms) */ + A_UINT32 ant_dur[WMI_MAX_CHAINS][WMI_ASD_MAX_ANTTYPE]; + /** RSSI on each antenna attached to each chain in dbm */ + A_INT32 ant_rssi[WMI_MAX_CHAINS][WMI_ASD_MAX_ANTTYPE]; } wmi_pdev_div_rssi_antid_event_fixed_param; typedef struct { @@ -11054,6 +11242,7 @@ typedef enum { WMI_REQUEST_VDEV_EXTD_STAT = 0x10000, WMI_REQUEST_PDEV_EXTD_STAT = 0x20000, WMI_REQUEST_PDEV_TELEMETRY_STAT = 0x40000, + WMI_REQUEST_VDEV_RECV_BCN_STAT = 0x80000, } wmi_stats_id; /* @@ -12044,6 +12233,10 @@ typedef struct { * wmi_pdev_telemetry_stats wmi_pdev_telemetry_stats[] * follows the other TLVs */ +/* If WMI_REQUEST_VDEV_RECV_BCN_STAT is set in stats_id, then TLV + * wmi_recv_bcn_stats wmi_recv_bcn_stats[] + * follows the other TLVs + */ } wmi_stats_event_fixed_param; /* WLAN channel CCA stats bitmap */ @@ -12545,8 +12738,20 @@ typedef struct { * * b'31-b'29 unused / reserved * b'28 indicate the version of rate-code (1 = RATECODE_V1) - * b'27-b'11 unused / reserved - * b'10-b'8 indicate the preamble (0 OFDM, 1 CCK, 2 HT, 3 VHT) + * b'27 unused / reserved + * b'26-b'19 indicate TX power (int8), with 0.25 dBm units + * b'15-b'14 indicate punctured mode as follows: + * 0: NO_PUNCTURE + * 1: PUNCTURED_20MHZ + * 2: PUNCTURED_40MHZ + * 3: PUNCTURED_80MHZ + * 4: PUNCTURED_120MHZ + * b'15-b'14 indicate the guard interval: + * 0: 800us, 1: 400us, 2: 1600us, 3: 3200us + * b'13-b'11 indicate the bandwidth: + * 0: 20MHz, 1: 40MHz, 2: 80MHz, 3: 160MHz, 4: 320MHz + * b'10-b'8 indicate the preamble: + * 0: OFDM, 1: CCK, 2: HT, 3: VHT, 4: HE, 5: EHT * b'7-b'5 indicate the NSS (0 - 1x1, 1 - 2x2, 2 - 3x3, 3 - 4x4) * b'4-b'0 indicate the rate, which is indicated as follows: * OFDM : 0: OFDM 48 Mbps @@ -12569,6 +12774,9 @@ typedef struct { * 0..7: MCS0..MCS7 (HT) * 0..9: MCS0..MCS9 (11AC VHT) * 0..11: MCS0..MCS11 (11AX VHT) + * HE/EHT (pream == 4/5) + * 0..13: MCS0..MCS13 (11AX EHT) + * 14..15: MCS14..MCS15 (EHT) */ /** rate-code of the last transmission */ A_UINT32 last_tx_rate_code; @@ -12692,224 +12900,240 @@ typedef struct { * number of vdev active count * (WMI_PDEV_STATS_VDEV_UP_CNT_SET,GET) */ - A_UINT32 opaque_debug_num_macs_phy_vdev_up_active; - /** refer wlan_pdev ic flags */ - A_UINT32 opaque_debug_ic_flags; - /** vdev_id that are paused per pdev */ - A_UINT32 opaque_debug_paused_ap_vdev_bitmap; - /** opaque_debug_flags: - * refer to WLAN_PS_DESC_BIN_HWM_HIT or WLAN_PS_DESC_BIN_LWM_HIT - */ - A_UINT32 opaque_debug_flags; - /** wlan_pdev fields remote_peer_cnt, max_rf_chains_2G and max_rf_chains_5G - * remote_peer_cnt_max_rf_chains_2G_5G: - * This word contains the following bitfields: - * max chains supported in the 2.4 GHz band - * (WMI_PDEV_STATS_MAX_RF_CHAIN_2G_SET,GET) - * max chains supported in the 5 GHz band, - * (WMI_PDEV_STATS_MAX_RF_CHAIN_5G_SET,GET) - * number of remote peers - * (WMI_PDEV_STATS_REMOTE_PEER_CNT_SET,GET) - */ - A_UINT32 opaque_debug_remote_peer_cnt_max_rf_chains_2G_5G; - /** wlan_pdev max HT Capability info, WMI_HT_CAP defines */ - A_UINT32 opaque_debug_max_ht_cap_info; - /** wlan_pdev max VHT capability info, WMI_VHT_CAP defines */ - A_UINT32 opaque_debug_max_vht_cap_info; - /** opaque_debug_max_vht_supp_mcs: - * wlan_pdev max VHT Supported MCS which is - * vht_supp_mcs_2G or vht_supp_mcs_5G - */ - A_UINT32 opaque_debug_max_vht_supp_mcs; - /** wlan_pdev max HE capability info, WMI_HE_CAP defines */ - A_UINT32 opaque_debug_max_he_cap_info; - A_UINT32 opaque_debug_max_he_cap_info_ext; - /** the nominal chain mask for transmit */ - A_UINT32 opaque_debug_tx_chain_mask; - /** the nominal chain mask for receive */ - A_UINT32 opaque_debug_rx_chain_mask; - /** opaque_debug_ema_flags: - * This word contains the following bitfields: - * ema_flags: ema_max_vap_cnt and ema_max_profile_period from wlan_pdev - * ema_max_vap_cnt- number of maximum EMA Tx vaps (VAPs having both - * VDEV_FLAGS_EMA_MODE and VDEV_FLAGS_TRANSMIT_AP set) - * (WMI_PDEV_STATS_EMA_MAX_VAP_CNT_SET,GET) - * ema_max_profile_period - maximum profile periodicity - * (maximum number of beacons after which VAP profiles repeat) - * for any EMA VAP on any pdev. - * (WMI_PDEV_STATS_EMA_MAX_PROFILE_PERIOD_SET,GET) - */ - A_UINT32 opaque_debug_ema_flags; - /** wlan_pdev - maximum ML peers supported */ - A_UINT32 opaque_debug_num_ml_peer_entries; - /** This word contains the following bitfields: - * wlan_pdev fields - num_max_hw_links, current_chip_id and max_num_chips - * (related to MLO) - * Max number of HW links - * (WMI_PDEV_STATS_NUM_MAX_HW_LINKS_SET,GET) - * Current Chip Id - * (WMI_PDEV_STATS_CURRENT_CHIP_ID_SET,GET) - * Max number of chips - * (WMI_PDEV_STATS_MAX_NUM_CHIPS_SET,GET) - */ - A_UINT32 opaque_debug_mlo_flags; - /** Indicate beacon size in bytes */ - A_UINT32 opaque_debug_large_bcn_size; - /** proposed by the host value of MSDUQs per each LinkView peer's TID */ - A_UINT32 opaque_debug_num_of_linkview_msduqs_per_tid; - /** bcn_filter_context variables */ - A_UINT32 opaque_debug_bcns_dropped; - A_UINT32 opaque_debug_bcns_recvd; - A_UINT32 opaque_debug_bcns_delivered; - /** Tids that are paused/unpaused based on module_id */ - A_UINT32 opaque_debug_vdev_all_tid_pause_bitmap; - /** Tids that are blocked/unblocked based on module_id */ - A_UINT32 opaque_debug_vdev_all_tid_block_bitmap; - /** wal_pdev rx filter, WAL_RX_FILTER_FLAGS defines */ - A_UINT32 opaque_debug_rx_filter; - /** This word contains the following bitfields: - * aggr_nonaggr_retry_th: - * wal_pdev fields - agg_retry_th and non_agg_retry_th - * This value holds max retry threshold up to which a Data packet - * will be retried when ack is not received. - * agg_retry_th - Threshold value used when aggregation is enabled - * (WMI_PDEV_STATS_AGG_RETRY_TH_SET,GET) - * non_agg_retry_th - Threshold value used for non-aggregation. - * (WMI_PDEV_STATS_NON_AGG_RETRY_TH_SET) - */ - A_UINT32 opaque_debug_aggr_nonaggr_retry_th; - /** This word contains the following bitfields: - * num_max_rx_ba_sessions: - * Number of rx BA session establised - * (WMI_PDEV_STATS_NUM_RX_BA_SESSIONS_SET,GET) - * Max number of rx BA session from wal_pdev - * (WMI_PDEV_STATS_MAX_RX_BA_SESSIONS_SET,GET) - */ - A_UINT32 opaque_debug_num_max_rx_ba_sessions; - /** It holds WHAL_CHANNEL_SWITCH_FLAGS values */ - A_UINT32 opaque_debug_chan_switch_flags; - /** reset_cause holds PDEV_RESET_CONSEC_FAILURE or PDEV_RESET_TXQ_TIMEOUT */ - A_UINT32 opaque_debug_consecutive_failure_reset_cause; - /** PPDU duration limit, in us */ - A_UINT32 opaque_debug_mu_ppdu_dur_limit_us; - /** pdev reset in progress */ - A_UINT32 opaque_debug_reset_in_progress; - /** wal_dev - vdev_migrate_state refer to WAL_VDEV_MIGRATE_STATE */ - A_UINT32 opaque_debug_vdev_migrate_state; - /** opaque_debug_rts_rc_flag: - * wal_pdev rts ratecode - this value reflects whatever - * WMI_PDEV_PARAM_RTS_FIXED_RATE value the host has specified for the pdev. - */ - A_UINT32 opaque_debug_rts_rc_flag; - /* Num of peer delete in progress */ - A_UINT32 opaque_debug_num_of_peer_delete_in_progress; - /** wal_pdev total number of active vdev count */ - A_UINT32 opaque_debug_total_active_vdev_cnt; - /** wal_pdev - max number of vdevs per pdev */ - A_UINT32 opaque_debug_max_vdevs; - /* NonOccupancyList(NOL) context */ - A_UINT32 opaque_debug_dfs_nol_count; - /** NOL timeout in seconds */ - A_UINT32 opaque_debug_dfs_nol_timeout; - A_UINT32 opaque_debug_dfs_use_nol; - /** channel availability check mode, refer enum WMI_ADFS_OCAC_MODE */ - A_UINT32 opaque_debug_cac_mode; - A_UINT32 opaque_debug_dyn_ppdu_dur; /* in ms */ - /** This word contains the following bitfields: - * wal_pdev home channel info - * home_chan_mhz_flags: - * primary channel frequency in mhz - * (WMI_PDEV_STATS_HOME_CHAN_MHZ_SET,GET) - * flags to specify other channel attributes - * (WMI_PDEV_STATS_HOME_CHAN_FLAGS_SET, GET) - */ - A_UINT32 opaque_debug_home_chan_mhz_flags; - /** home channel center frequency in MHz */ - A_UINT32 opaque_debug_home_band_center_freq; - /** home channel phy_mode, refer enum WLAN_PHY_MODE */ - A_UINT32 opaque_debug_home_phy_mode; - /** This word contains the following bitfields: - * wal_pdev current channel info - * cur_chan_mhz_flags: - * primary channel frequency in mhz - * (WMI_PDEV_STATS_CUR_CHAN_MHZ_SET,GET) - * flags to specify other channel attributes - * (WMI_PDEV_STATS_CUR_CHAN_FLAGS_SET,GET) - */ - A_UINT32 opaque_debug_cur_chan_mhz_flags; - /** current channel center frequency in MHz */ - A_UINT32 opaque_debug_cur_band_center_freq; - /** current channel phy_mode, refer enum WLAN_PHY_MODE */ - A_UINT32 opaque_debug_cur_phy_mode; - /* Beacon context info */ - A_UINT32 opaque_debug_bcn_q_num_bcns_queued_to_hw; - /** beacon queue AIFS */ - A_UINT32 opaque_debug_aifs; - /** beacon queue cwmin */ - A_UINT32 opaque_debug_cwmin; - /** beacon queue cwmax */ - A_UINT32 opaque_debug_cwmax; - /** FILS discovery period in TU */ - A_UINT32 opaque_debug_fils_period; - /** Beacon interval in TU */ - A_UINT32 opaque_debug_beacon_period; - A_UINT32 opaque_debug_staggered_beacon_intvl; - /** wal_pdev tx context, refer enum WAL_TX_CTXT_FLAGS */ - A_UINT32 opaque_debug_tx_ctxt_flags; - /** opaque_debug_burst_mode_pending_isr - * wal_pdev tx_ctxt fields - burst_mode refer enum WAL_TX_BURST_MODE - * and pending_isr_status count - */ - A_UINT32 opaque_debug_burst_mode_pending_isr; - /** max burst duration from ppdu duration in us */ - A_UINT32 opaque_debug_burst_dur; - /** counter for tx hw stuck */ - A_UINT32 opaque_debug_tx_hw_stuck_cnt; - /** counter for tx consecutive lifetime expiry */ - A_UINT32 opaque_debug_consecutive_lifetime_expiries; - /** wal_pdev rx context, refer enum WAL_RX_CTXT_FLAGS */ - A_UINT32 opaque_debug_rx_ctxt_flags; - /** wal_pdev fields in rx context for rx_suspend or resume count */ - A_UINT32 opaque_debug_rx_suspend_cnt; - A_UINT32 opaque_debug_rx_resume_cnt; - A_UINT32 opaque_debug_rx_pcie_suspend_cnt; - A_UINT32 opaque_debug_rx_pcie_resume_cnt; - /** This word contains the following bitfields: - * wal_pdev fields - * pdev paused - WMI_PDEV_STATS_PAUSED_SET,GET - * pdev suspend - WMI_PDEV_STATS_SUSPENDED_SET,GET - * cac_enabed - MI_PDEV_STATS_CAC_ENABLED_SET,GET - * monitor VAP present - WMI_PDEV_STATS_IS_MONITOR_TYPE_PRESENT_SET,GET - * beacon tx mode - WMI_PDEV_STATS_BCN_TX_MODE_SET,GET - * isTXsuspended - WMI_PDEV_STATS_IS_TXSUSPENDED_SET,GET - * isSCHEDsuspended - WMI_PDEV_STATS_IS_SCHEDSUSPENDED_SET,GET - * sched_algo_resume_needed - - * WMI_PDEV_STATS_SCHED_ALGO_RESUME_NEEDED_SET,GET - * abort_reason - WMI_PDEV_STATS_ABORT_REASON_SET,GET - * atf_cfg - WMI_PDEV_STATS_ATF_CONFIG_SET,GET - * Green AP TX chainmask valid - WMI_PDEV_STATS_GAP_TX_CH_MASK_VALID_SET,GET - * Green AP RX chainmask valid - WMI_PDEV_STATS_GAP_RX_CH_MASK_VALID_SET,GET - * Green AP Phy mode valid - WMI_PDEV_STATS_GAP_PHY_MODE_VALID_SET,GET - * burst_enable - WMI_PDEV_STATS_BURST_ENABLE_SET,GET - */ - A_UINT32 opaque_debug_wal_pdev_bitfield; - /** This word contains the following bitfields: - * gap_phy_mode_freq: - * When GreenAP is enabled, phy_mode (WMI_PDEV_STATS_GAP_PHY_MODE_SET,GET) - * and center freq(MHz) (WMI_PDEV_STATS_GAP_BAND_CENTER_FREQ1_SET,GET) - * in GAP context is displayed - */ - A_UINT32 opaque_debug_gap_phy_mode_freq; - /** - * The following 5 opaque_debug_reserved_field variables are provided - * purely for debugging by technicians who have outside knowledge of - * what kind of values the target has placed into these fields. - */ - A_UINT32 opaque_debug_reserved_field_1; - A_UINT32 opaque_debug_reserved_field_2; - A_UINT32 opaque_debug_reserved_field_3; - A_UINT32 opaque_debug_reserved_field_4; - A_UINT32 opaque_debug_reserved_field_5; + A_UINT32 opaque_debug_num_macs_phy_vdev_up_active; + /** refer wlan_pdev ic flags */ + A_UINT32 opaque_debug_ic_flags; + /** vdev_id that are paused per pdev */ + A_UINT32 opaque_debug_paused_ap_vdev_bitmap; + /** opaque_debug_flags: + * refer to WLAN_PS_DESC_BIN_HWM_HIT or WLAN_PS_DESC_BIN_LWM_HIT + */ + A_UINT32 opaque_debug_flags; + /** wlan_pdev fields remote_peer_cnt, max_rf_chains_2G and max_rf_chains_5G + * remote_peer_cnt_max_rf_chains_2G_5G: + * This word contains the following bitfields: + * max chains supported in the 2.4 GHz band + * (WMI_PDEV_STATS_MAX_RF_CHAIN_2G_SET,GET) + * max chains supported in the 5 GHz band, + * (WMI_PDEV_STATS_MAX_RF_CHAIN_5G_SET,GET) + * number of remote peers + * (WMI_PDEV_STATS_REMOTE_PEER_CNT_SET,GET) + */ + A_UINT32 opaque_debug_remote_peer_cnt_max_rf_chains_2G_5G; + /** wlan_pdev max HT Capability info, WMI_HT_CAP defines */ + A_UINT32 opaque_debug_max_ht_cap_info; + /** wlan_pdev max VHT capability info, WMI_VHT_CAP defines */ + A_UINT32 opaque_debug_max_vht_cap_info; + /** opaque_debug_max_vht_supp_mcs: + * wlan_pdev max VHT Supported MCS which is + * vht_supp_mcs_2G or vht_supp_mcs_5G + */ + A_UINT32 opaque_debug_max_vht_supp_mcs; + /** wlan_pdev max HE capability info, WMI_HE_CAP defines */ + A_UINT32 opaque_debug_max_he_cap_info; + A_UINT32 opaque_debug_max_he_cap_info_ext; + /** the nominal chain mask for transmit */ + A_UINT32 opaque_debug_tx_chain_mask; + /** the nominal chain mask for receive */ + A_UINT32 opaque_debug_rx_chain_mask; + /** opaque_debug_ema_flags: + * This word contains the following bitfields: + * ema_flags: ema_max_vap_cnt and ema_max_profile_period from wlan_pdev + * ema_max_vap_cnt- number of maximum EMA Tx vaps (VAPs having both + * VDEV_FLAGS_EMA_MODE and VDEV_FLAGS_TRANSMIT_AP set) + * (WMI_PDEV_STATS_EMA_MAX_VAP_CNT_SET,GET) + * ema_max_profile_period - maximum profile periodicity + * (maximum number of beacons after which VAP profiles repeat) + * for any EMA VAP on any pdev. + * (WMI_PDEV_STATS_EMA_MAX_PROFILE_PERIOD_SET,GET) + */ + A_UINT32 opaque_debug_ema_flags; + /** wlan_pdev - maximum ML peers supported */ + A_UINT32 opaque_debug_num_ml_peer_entries; + /** This word contains the following bitfields: + * wlan_pdev fields - num_max_hw_links, current_chip_id and max_num_chips + * (related to MLO) + * Max number of HW links + * (WMI_PDEV_STATS_NUM_MAX_HW_LINKS_SET,GET) + * Current Chip Id + * (WMI_PDEV_STATS_CURRENT_CHIP_ID_SET,GET) + * Max number of chips + * (WMI_PDEV_STATS_MAX_NUM_CHIPS_SET,GET) + */ + A_UINT32 opaque_debug_mlo_flags; + /** Indicate beacon size in bytes */ + A_UINT32 opaque_debug_large_bcn_size; + /** proposed by the host value of MSDUQs per each LinkView peer's TID */ + A_UINT32 opaque_debug_num_of_linkview_msduqs_per_tid; + /** bcn_filter_context variables */ + A_UINT32 opaque_debug_bcns_dropped; + A_UINT32 opaque_debug_bcns_recvd; + A_UINT32 opaque_debug_bcns_delivered; + /** + * Lower 32 bits bitmap of pause ids for TIDs that are paused/unpaused + * based on module_id. + */ + A_UINT32 opaque_debug_vdev_all_tid_pause_bitmap; + /** + * Lower 32 bits bitmap of pause ids for TIDs that are blocked/unblocked + * based on module_id. + */ + A_UINT32 opaque_debug_vdev_all_tid_block_bitmap; + /** wal_pdev rx filter, WAL_RX_FILTER_FLAGS defines */ + A_UINT32 opaque_debug_rx_filter; + /** This word contains the following bitfields: + * aggr_nonaggr_retry_th: + * wal_pdev fields - agg_retry_th and non_agg_retry_th + * This value holds max retry threshold up to which a Data packet + * will be retried when ack is not received. + * agg_retry_th - Threshold value used when aggregation is enabled + * (WMI_PDEV_STATS_AGG_RETRY_TH_SET,GET) + * non_agg_retry_th - Threshold value used for non-aggregation. + * (WMI_PDEV_STATS_NON_AGG_RETRY_TH_SET) + */ + A_UINT32 opaque_debug_aggr_nonaggr_retry_th; + /** This word contains the following bitfields: + * num_max_rx_ba_sessions: + * Number of rx BA session establised + * (WMI_PDEV_STATS_NUM_RX_BA_SESSIONS_SET,GET) + * Max number of rx BA session from wal_pdev + * (WMI_PDEV_STATS_MAX_RX_BA_SESSIONS_SET,GET) + */ + A_UINT32 opaque_debug_num_max_rx_ba_sessions; + /** It holds WHAL_CHANNEL_SWITCH_FLAGS values */ + A_UINT32 opaque_debug_chan_switch_flags; + /** reset_cause holds PDEV_RESET_CONSEC_FAILURE or PDEV_RESET_TXQ_TIMEOUT */ + A_UINT32 opaque_debug_consecutive_failure_reset_cause; + /** PPDU duration limit, in us */ + A_UINT32 opaque_debug_mu_ppdu_dur_limit_us; + /** pdev reset in progress */ + A_UINT32 opaque_debug_reset_in_progress; + /** wal_dev - vdev_migrate_state refer to WAL_VDEV_MIGRATE_STATE */ + A_UINT32 opaque_debug_vdev_migrate_state; + /** opaque_debug_rts_rc_flag: + * wal_pdev rts ratecode - this value reflects whatever + * WMI_PDEV_PARAM_RTS_FIXED_RATE value the host has specified for the pdev. + */ + A_UINT32 opaque_debug_rts_rc_flag; + /* Num of peer delete in progress */ + A_UINT32 opaque_debug_num_of_peer_delete_in_progress; + /** wal_pdev total number of active vdev count */ + A_UINT32 opaque_debug_total_active_vdev_cnt; + /** wal_pdev - max number of vdevs per pdev */ + A_UINT32 opaque_debug_max_vdevs; + /* NonOccupancyList(NOL) context */ + A_UINT32 opaque_debug_dfs_nol_count; + /** NOL timeout in seconds */ + A_UINT32 opaque_debug_dfs_nol_timeout; + A_UINT32 opaque_debug_dfs_use_nol; + /** channel availability check mode, refer enum WMI_ADFS_OCAC_MODE */ + A_UINT32 opaque_debug_cac_mode; + A_UINT32 opaque_debug_dyn_ppdu_dur; /* in ms */ + /** This word contains the following bitfields: + * wal_pdev home channel info + * home_chan_mhz_flags: + * primary channel frequency in mhz + * (WMI_PDEV_STATS_HOME_CHAN_MHZ_SET,GET) + * flags to specify other channel attributes + * (WMI_PDEV_STATS_HOME_CHAN_FLAGS_SET, GET) + */ + A_UINT32 opaque_debug_home_chan_mhz_flags; + /** home channel center frequency in MHz */ + A_UINT32 opaque_debug_home_band_center_freq; + /** home channel phy_mode, refer enum WLAN_PHY_MODE */ + A_UINT32 opaque_debug_home_phy_mode; + /** This word contains the following bitfields: + * wal_pdev current channel info + * cur_chan_mhz_flags: + * primary channel frequency in mhz + * (WMI_PDEV_STATS_CUR_CHAN_MHZ_SET,GET) + * flags to specify other channel attributes + * (WMI_PDEV_STATS_CUR_CHAN_FLAGS_SET,GET) + */ + A_UINT32 opaque_debug_cur_chan_mhz_flags; + /** current channel center frequency in MHz */ + A_UINT32 opaque_debug_cur_band_center_freq; + /** current channel phy_mode, refer enum WLAN_PHY_MODE */ + A_UINT32 opaque_debug_cur_phy_mode; + /* Beacon context info */ + A_UINT32 opaque_debug_bcn_q_num_bcns_queued_to_hw; + /** beacon queue AIFS */ + A_UINT32 opaque_debug_aifs; + /** beacon queue cwmin */ + A_UINT32 opaque_debug_cwmin; + /** beacon queue cwmax */ + A_UINT32 opaque_debug_cwmax; + /** FILS discovery period in TU */ + A_UINT32 opaque_debug_fils_period; + /** Beacon interval in TU */ + A_UINT32 opaque_debug_beacon_period; + A_UINT32 opaque_debug_staggered_beacon_intvl; + /** wal_pdev tx context, refer enum WAL_TX_CTXT_FLAGS */ + A_UINT32 opaque_debug_tx_ctxt_flags; + /** opaque_debug_burst_mode_pending_isr + * wal_pdev tx_ctxt fields - burst_mode refer enum WAL_TX_BURST_MODE + * and pending_isr_status count + */ + A_UINT32 opaque_debug_burst_mode_pending_isr; + /** max burst duration from ppdu duration in us */ + A_UINT32 opaque_debug_burst_dur; + /** counter for tx hw stuck */ + A_UINT32 opaque_debug_tx_hw_stuck_cnt; + /** counter for tx consecutive lifetime expiry */ + A_UINT32 opaque_debug_consecutive_lifetime_expiries; + /** wal_pdev rx context, refer enum WAL_RX_CTXT_FLAGS */ + A_UINT32 opaque_debug_rx_ctxt_flags; + /** wal_pdev fields in rx context for rx_suspend or resume count */ + A_UINT32 opaque_debug_rx_suspend_cnt; + A_UINT32 opaque_debug_rx_resume_cnt; + A_UINT32 opaque_debug_rx_pcie_suspend_cnt; + A_UINT32 opaque_debug_rx_pcie_resume_cnt; + /** This word contains the following bitfields: + * wal_pdev fields + * pdev paused - WMI_PDEV_STATS_PAUSED_SET,GET + * pdev suspend - WMI_PDEV_STATS_SUSPENDED_SET,GET + * cac_enabed - MI_PDEV_STATS_CAC_ENABLED_SET,GET + * monitor VAP present - WMI_PDEV_STATS_IS_MONITOR_TYPE_PRESENT_SET,GET + * beacon tx mode - WMI_PDEV_STATS_BCN_TX_MODE_SET,GET + * isTXsuspended - WMI_PDEV_STATS_IS_TXSUSPENDED_SET,GET + * isSCHEDsuspended - WMI_PDEV_STATS_IS_SCHEDSUSPENDED_SET,GET + * sched_algo_resume_needed - + * WMI_PDEV_STATS_SCHED_ALGO_RESUME_NEEDED_SET,GET + * abort_reason - WMI_PDEV_STATS_ABORT_REASON_SET,GET + * atf_cfg - WMI_PDEV_STATS_ATF_CONFIG_SET,GET + * Green AP TX chainmask valid- WMI_PDEV_STATS_GAP_TX_CH_MASK_VALID_SET,GET + * Green AP RX chainmask valid- WMI_PDEV_STATS_GAP_RX_CH_MASK_VALID_SET,GET + * Green AP Phy mode valid - WMI_PDEV_STATS_GAP_PHY_MODE_VALID_SET,GET + * burst_enable - WMI_PDEV_STATS_BURST_ENABLE_SET,GET + */ + A_UINT32 opaque_debug_wal_pdev_bitfield; + /** This word contains the following bitfields: + * gap_phy_mode_freq: + * When GreenAP is enabled, phy_mode (WMI_PDEV_STATS_GAP_PHY_MODE_SET,GET) + * and center freq(MHz) (WMI_PDEV_STATS_GAP_BAND_CENTER_FREQ1_SET,GET) + * in GAP context is displayed + */ + A_UINT32 opaque_debug_gap_phy_mode_freq; + /** + * The following 5 opaque_debug_reserved_field variables are provided + * purely for debugging by technicians who have outside knowledge of + * what kind of values the target has placed into these fields. + */ + A_UINT32 opaque_debug_reserved_field_1; + A_UINT32 opaque_debug_reserved_field_2; + A_UINT32 opaque_debug_reserved_field_3; + A_UINT32 opaque_debug_reserved_field_4; + A_UINT32 opaque_debug_reserved_field_5; + /** + * Upper 32 bits bitmap of pause ids for TIDs that are paused/unpaused + * based on module_id. + */ + A_UINT32 opaque_debug_vdev_all_tid_pause_bitmap_ext; + /** + * Upper 32 bits bitmap of pause ids for TIDs that are blocked/unblocked + * based on module_id. + */ + A_UINT32 opaque_debug_vdev_all_tid_block_bitmap_ext; } wmi_ctrl_path_pdev_stats_struct; #define WMI_PDEV_STATS_NUM_MACS_GET(flag) \ @@ -13444,6 +13668,7 @@ typedef enum { WMI_CTRL_PATH_STATS_CAL_TYPE_PADROOP = 0x17, WMI_CTRL_PATH_STATS_CAL_TYPE_SELFCALTPC = 0x18, WMI_CTRL_PATH_STATS_CAL_TYPE_RXSPUR = 0x19, + WMI_CTRL_PATH_STATS_CAL_TYPE_PDADC = 0x1a, /* add new cal types above this line */ WMI_CTRL_PATH_STATS_CAL_TYPE_INVALID = 0xFF @@ -13543,6 +13768,7 @@ static INLINE A_UINT8 *wmi_ctrl_path_cal_type_id_to_name(A_UINT32 cal_type_id) WMI_RETURN_STRING(WMI_CTRL_PATH_STATS_CAL_TYPE_PADROOP); WMI_RETURN_STRING(WMI_CTRL_PATH_STATS_CAL_TYPE_SELFCALTPC); WMI_RETURN_STRING(WMI_CTRL_PATH_STATS_CAL_TYPE_RXSPUR); + WMI_RETURN_STRING(WMI_CTRL_PATH_STATS_CAL_TYPE_PDADC); } return (A_UINT8 *) "WMI_CTRL_PATH_STATS_CAL_TYPE_UNKNOWN"; @@ -14364,28 +14590,28 @@ typedef struct { */ A_UINT32 opaque_debug_keyid0_ast_index; /* opaque_debug_all_tids_block_module_bitmap: - * Bitmap of block IDs requesting block of all TIDs, + * Lower 32 bits bitmap of block IDs requesting block of all TIDs, * part of wal_peer. * Refer to enum WLAN_PAUSE_ID. * This block/pause ID can be mapped to a WLAN_MODULE_ID module ID. */ A_UINT32 opaque_debug_all_tids_block_module_bitmap; /* opaque_debug_all_tids_pause_module_bitmap: - * Bitmap of pause IDs requesting block of all TIDs, + * Lower 32 bits bitmap of pause IDs requesting block of all TIDs, * part of wal_peer. * Refer to enum WLAN_PAUSE_ID. * This pause ID can be mapped to a WLAN_MODULE_ID module ID. */ A_UINT32 opaque_debug_all_tids_pause_module_bitmap; /* opaque_debug_data_tids_block_module_bitmap: - * Bitmap of block ids requesting block of data tids, + * Lower 32 bits bitmap of block ids requesting block of data tids, * part of wal_peer. * Refer to enum WLAN_PAUSE_ID. * This block/pause ID can be mapped to a WLAN_MODULE_ID module ID. */ A_UINT32 opaque_debug_data_tids_block_module_bitmap; /* opaque_debug_data_tids_pause_module_bitmap: - * Bitmap of pause ids requesting block of data tids, + * Lower 32 bits bitmap of pause ids requesting block of data tids, * part of wal_peer. * Refer to enum WLAN_PAUSE_ID. * This pause ID can be mapped to a WLAN_MODULE_ID module ID. @@ -14778,6 +15004,49 @@ typedef struct { A_UINT32 opaque_debug_field_2; A_UINT32 opaque_debug_field_3; A_UINT32 opaque_debug_field_4; + + /* ba_stats + * This word contains the following bitfields: + * bits 15:0 - ba_tx_neg_fail: Blockack Transmit Negotiation failure + * count for the all TIDs in peer. + * Use WMI_PEER_STATS_BA_TX_NEG_FAIL_SET,GET macros. + * bits 31:16 - reserved + */ + union { + A_UINT32 ba_stats__word; + struct { + A_UINT32 ba_tx_neg_fail: 16, + reserved: 16; + }; + }; + /* opaque_debug_all_tids_block_module_bitmap_ext: + * Upper 32 bits bitmap of block IDs requesting block of all TIDs, + * part of wal_peer. + * Refer to enum WLAN_PAUSE_ID. + * This block/pause ID can be mapped to a WLAN_MODULE_ID module ID. + */ + A_UINT32 opaque_debug_all_tids_block_module_bitmap_ext; + /* opaque_debug_all_tids_pause_module_bitmap_ext: + * Upper 32 bits bitmap of pause IDs requesting pause of all TIDs, + * part of wal_peer. + * Refer to enum WLAN_PAUSE_ID. + * This pause ID can be mapped to a WLAN_MODULE_ID module ID. + */ + A_UINT32 opaque_debug_all_tids_pause_module_bitmap_ext; + /* opaque_debug_data_tids_block_module_bitmap_ext: + * Upper 32 bits bitmap of block ids requesting block of data tids, + * part of wal_peer. + * Refer to enum WLAN_PAUSE_ID. + * This block/pause ID can be mapped to a WLAN_MODULE_ID module ID. + */ + A_UINT32 opaque_debug_data_tids_block_module_bitmap_ext; + /* opaque_debug_data_tids_pause_module_bitmap_ext: + * Upper 32 bits bitmap of pause ids requesting pause of data tids, + * part of wal_peer. + * Refer to enum WLAN_PAUSE_ID. + * This pause ID can be mapped to a WLAN_MODULE_ID module ID. + */ + A_UINT32 opaque_debug_data_tids_pause_module_bitmap_ext; } wmi_ctrl_path_peer_stats_struct; #define WMI_PEER_STATS_SM_MASK_SET(flag, val) \ @@ -15102,6 +15371,12 @@ typedef struct { #define WMI_PEER_STATS_RC_CHAN_FREQ_GET(flag) \ WMI_GET_BITS(flag, 16, 16) +#define WMI_PEER_STATS_PEER_BA_TX_NEG_FAIL_SET(flag, val) \ + WMI_SET_BITS(flag, 0, 16, val) +#define WMI_PEER_STATS_PEER_BA_TX_NEG_FAIL_GET(flag) \ + WMI_GET_BITS(flag, 0, 16) +/* bits 31:16 unused/reserved */ + typedef struct { /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_ctrl_path_cfr_stats_struct */ @@ -15326,6 +15601,30 @@ typedef struct{ A_UINT32 estimated_air_time_per_ac; } wmi_pdev_telemetry_stats; + +#define WMI_MAX_BCN_HISTORY 10 /* max beacon history entry */ + +typedef struct { + /* Beacon real RSSI, non-averaged rssi, dBm units; -128 means invalid */ + A_INT32 bcn_rssi; + /* Beacon tsf, 0 means invalid */ + A_UINT32 bcn_tsf; + /* NOTE: + * Due to backwards-compatibility requirements, no new fields + * can be added to this struct. + */ +} wmi_bcn_his_info; + +typedef struct { + /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_recv_bcn_stats */ + A_UINT32 tlv_header; + /* Vdev id */ + A_UINT32 vdev_id; + /* Received History of last ten Beacon of the connected Bss */ + wmi_bcn_his_info bcn_history[WMI_MAX_BCN_HISTORY]; +} wmi_recv_bcn_stats; + + /** * VDEV statistics * @todo @@ -15494,9 +15793,15 @@ typedef struct { A_UINT32 opaque_debug_rx_pkt_on_channel; /* Contains the value of Target beacon transmission time offset value */ A_UINT32 opaque_debug_tbtt_offset; - /* Contains the value of tid pause bitmap of the peer from wal_vdev */ + /* + * Contains the value of lower 32 bits of tid pause bitmap of the peer + * from wal_vdev + */ A_UINT32 opaque_debug_peer_all_tid_pause_bitmap; - /* Contains the value of tid block bitmap of the peer from wal_vdev */ + /* + * Contains the value of lower 32 bits of tid block bitmap of the peer + * from wal_vdev + */ A_UINT32 opaque_debug_peer_all_tid_block_bitmap; /* Contains the value of tdls peer kickout threshold */ A_UINT32 opaque_debug_tdls_peer_kickout_th; @@ -15850,6 +16155,16 @@ typedef struct { A_UINT32 opaque_debug_field_2; A_UINT32 opaque_debug_field_3; A_UINT32 opaque_debug_field_4; + /* + * Contains the value of upper 32 bits tid pause bitmap of the peer + * from wal_vdev + */ + A_UINT32 opaque_debug_peer_all_tid_pause_bitmap_ext; + /* + * Contains the value of upper 32 bits tid block bitmap of the peer + * from wal_vdev + */ + A_UINT32 opaque_debug_peer_all_tid_block_bitmap_ext; } wmi_ctrl_path_vdev_stats_struct; @@ -16595,6 +16910,40 @@ typedef struct { A_UINT32 dot11RTSFailureCount; } wmi_ctrl_path_sta_rrm_stats_struct; +typedef struct { + /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_ctrl_path_sta_dar_stats_struct */ + A_UINT32 tlv_header; + A_UINT32 vdev_id; + A_UINT32 stats_granularity; /* Possible values are listed in wmi_ctrl_path_stats_granularity enum. */ + /* transmit_pwr: + * Units are dB w.r.t. a -20 dBm reference. + * For example, if the STA's tx power is 10 dBm, the transmit_pwr field's + * value will be 30. + */ + A_UINT32 transmit_pwr; + A_UINT32 cca_busy_cnt; + A_UINT32 cycle_cnt; + /* + * For the below 8-element arrays, in case of AC-level granularity, + * only the first 4 elements of the array are populated, and are indexed + * by wmi_traffic_ac enum values. + * Otherwise, for TID level granularity all 8 elements of the array will + * be filled by FW, and are indexed by the TID value. + */ + A_UINT32 success_mpdu_tx_cnt[8]; + A_UINT32 dropped_mpdu_tx_cnt[8]; + A_UINT32 rts_success_cnt[8]; + A_UINT32 rts_fail_cnt[8]; + A_UINT32 fcs_fail_cnt[8]; /* number of rx MPDUs whose FCS check failed */ + /* ack_fail_cnt: + * number of tx MPDUs nacked within a block ack, + * or for which no block ack was received. + */ + A_UINT32 ack_fail_cnt[8]; + A_UINT32 ba_nego_fail_cnt; + A_UINT32 beacon_loss_cnt; +} wmi_ctrl_path_sta_dar_stats_struct; + typedef struct { /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_ctrl_path_vdev_bcn_stats_struct */ A_UINT32 tlv_header; @@ -17050,6 +17399,8 @@ typedef struct { #define WMI_MLO_FLAGS_SET_IEEE_LINK_ID_VALID(mlo_flags, value) WMI_SET_BITS(mlo_flags, 18, 1, value) #define WMI_MLO_FLAGS_GET_IEEE_LINK_ID_VALID_PARTNER(mlo_flags) WMI_GET_BITS(mlo_flags, 19, 1) #define WMI_MLO_FLAGS_SET_IEEE_LINK_ID_VALID_PARTNER(mlo_flags, value) WMI_SET_BITS(mlo_flags, 19, 1, value) +#define WMI_MLO_FLAGS_GET_SINGLE_LINK_EMLSR_EN(mlo_flags) WMI_GET_BITS(mlo_flags, 20, 1) +#define WMI_MLO_FLAGS_SET_SINGLE_LINK_EMLSR_EN(mlo_flags, value) WMI_SET_BITS(mlo_flags, 20, 1, value) /* this structure used for passing MLO flags */ typedef struct { @@ -17081,7 +17432,8 @@ typedef struct { start_as_active:1, /* indicate link should be started in active status */ mlo_ieee_link_id_valid:1, /* indicate if the ieee_link_id in wmi_vdev_start_mlo_params is valid */ mlo_ieee_link_id_valid_partner:1, /* indicate if the ieee_link_id in wmi_partner_link_params is valid */ - unused: 12; + single_link_emlsr_en:1, /* indicate if emlsr enablement on one link is supported */ + unused: 11; }; A_UINT32 mlo_flags; }; @@ -17911,6 +18263,10 @@ typedef enum { #define WMI_HECAP_MAC_HTVHTTRIGRX_GET_D2(he_cap2) (0) #define WMI_HECAP_MAC_HTVHTTRIGRX_SET_D2(he_cap2, value) {;} +#define WMI_GET_HW_RATECODE_VERSION(_rcode) (((_rcode) >> 28) & 0x1) +#define WMI_SET_HW_RATECODE_VERSION_V1(_rcode) (((1) << 28) | (_rcode)) +#define WMI_GET_HW_RATECODE_GI_V1(_rcode) (((_rcode) >> 14) & 0x3) +#define WMI_GET_HW_RATECODE_BW_V1(_rcode) (((_rcode) >> 11) & 0x7) #define WMI_GET_HW_RATECODE_PREAM_V1(_rcode) (((_rcode) >> 8) & 0x7) #define WMI_GET_HW_RATECODE_NSS_V1(_rcode) (((_rcode) >> 5) & 0x7) #define WMI_GET_HW_RATECODE_RATE_V1(_rcode) (((_rcode) >> 0) & 0x1F) @@ -17976,6 +18332,8 @@ typedef struct { /* Target TSF value by which VDEV restart procedure should be completed in FW */ A_UINT32 target_tsf_us_lo; /* bits 31:0 */ A_UINT32 target_tsf_us_hi; /* bits 63:32 */ + A_UINT32 vdev_op_ul_nss; /* vdev operating uplink nss. 1 ~ n: 1ss ~ nss */ + A_UINT32 vdev_op_dl_nss; /* vdev operating downlink nss. 1 ~ n: 1ss ~ nss */ /* The TLVs follows this structure: * wmi_channel chan; <-- WMI channel @@ -18035,6 +18393,12 @@ enum WMI_VDEV_UP_FLAGS { WMI_VDEV_UP_FLAG_VBSS_PASSIVE = 0x00000004, }; +typedef struct{ + A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_co_located_chan_info */ + /* co-located link frequency discovered on connected ap */ + A_UINT32 link_freq; /* MHz units */ +} wmi_co_located_chan_info; + typedef struct { A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vdev_up_cmdid_fixed_param */ /** unique id identifying the VDEV, generated by the caller */ @@ -18051,6 +18415,10 @@ typedef struct { A_UINT32 profile_num; /** flags - this is a bitwise-or combination of WMI_VDEV_UP_FLAGS values */ A_UINT32 flags; + /* The below TLVs follow this struct: + * - wmi_co_located_chan_info co_located_chan_info[]; + * Connected AP's co-located channel info + */ } wmi_vdev_up_cmd_fixed_param; typedef struct { @@ -18175,6 +18543,7 @@ typedef enum { WMI_RATE_PREAMBLE_VHT, WMI_RATE_PREAMBLE_HE, WMI_RATE_PREAMBLE_EHT, + WMI_RATE_PREAMBLE_UHR, } WMI_RATE_PREAMBLE; /** Value to disable fixed rate setting */ @@ -19308,6 +19677,14 @@ typedef enum { */ WMI_VDEV_PARAM_CONNECT_EXT_FEATURES, /* 0xC7 */ + /* Allow to tear down TWT on scan start, if corresponding INI is set */ + WMI_VDEV_PARAM_DISABLE_SCAN_START_TWT, /* 0xC8 */ + + /* + * value 0 | default value no opp | controlled from pdev level + * value 1 | disable responder for this vdev + */ + WMI_VDEV_PARAM_TWT_RESP_DISABLE, /* 0xC9 */ /*=== ADD NEW VDEV PARAM TYPES ABOVE THIS LINE === * The below vdev param types are used for prototyping, and are @@ -19474,6 +19851,13 @@ typedef enum { */ WMI_VDEV_PARAM_SET_SAP_PS_WITH_TWT, /* 0x8013 */ + /* + * Support RTT Bandwidth downgrade + * 0 - Disable RTT Bandwidth downgrade + * 1 - Enable RTT Bandwidth downgrade + */ + WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_BW_DOWNGRADE, /* 0x8014 */ + /*=== END VDEV_PARAM_PROTOTYPE SECTION ===*/ } WMI_VDEV_PARAM; @@ -20320,91 +20704,96 @@ enum wmi_sta_ps_scheme_cfg { WMI_STA_PS_OPM_CONSERVATIVE = 0, WMI_STA_PS_OPM_AGGRESSIVE = 1, WMI_STA_PS_USER_DEF = 2, + WMI_STA_PS_LATENCY_DEF = 3, }; enum wmi_sta_powersave_param { -/** - * Controls how frames are retrievd from AP while STA is sleeping - * - * (see enum wmi_sta_ps_param_rx_wake_policy) - */ -WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + /** + * Controls how frames are retrievd from AP while STA is sleeping + * + * (see enum wmi_sta_ps_param_rx_wake_policy) + */ + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, -/** - * The STA will go active after this many TX - * - * (see enum wmi_sta_ps_param_tx_wake_threshold) - */ -WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + /** + * The STA will go active after this many TX + * + * (see enum wmi_sta_ps_param_tx_wake_threshold) + */ + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, -/** - * Number of PS-Poll to send before STA wakes up - * - * (see enum wmi_sta_ps_param_pspoll_count) - * - */ -WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + /** + * Number of PS-Poll to send before STA wakes up + * + * (see enum wmi_sta_ps_param_pspoll_count) + * + */ + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, -/** - * TX/RX inactivity time in msec before going to sleep. - * - * The power save SM will monitor tx/rx activity on the VDEV, if no - * activity for the specified msec of the parameter the Power save SM will - * go to sleep. - */ -WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + /** + * TX/RX inactivity time in msec before going to sleep. + * + * The power save SM will monitor tx/rx activity on the VDEV, if no + * activity for the specified msec of the parameter the Power save SM will + * go to sleep. + */ + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, -/** - * Set uapsd configuration. - * - * (see enum wmi_sta_ps_param_uapsd) - */ -WMI_STA_PS_PARAM_UAPSD = 4, + /** + * Set uapsd configuration. + * + * (see enum wmi_sta_ps_param_uapsd) + */ + WMI_STA_PS_PARAM_UAPSD = 4, -/** - * Number of PS-Poll to send before STA wakes up in QPower Mode - */ -WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT = 5, + /** + * Number of PS-Poll to send before STA wakes up in QPower Mode + */ + WMI_STA_PS_PARAM_QPOWER_PSPOLL_COUNT = 5, -/** - * Enable OPM - */ -WMI_STA_PS_ENABLE_QPOWER = 6, - WMI_STA_PS_ENABLE_OPM = WMI_STA_PS_ENABLE_QPOWER, /* alias */ + /** + * Enable OPM + */ + WMI_STA_PS_ENABLE_QPOWER = 6, + WMI_STA_PS_ENABLE_OPM = WMI_STA_PS_ENABLE_QPOWER, /* alias */ -/** - * Number of TX frames before the entering the Active state - */ -WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE = 7, + /** + * Number of TX frames before the entering the Active state + */ + WMI_STA_PS_PARAM_QPOWER_MAX_TX_BEFORE_WAKE = 7, -/** - * QPower SPEC PSPOLL interval - */ -WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL = 8, + /** + * QPower SPEC PSPOLL interval + */ + WMI_STA_PS_PARAM_QPOWER_SPEC_PSPOLL_WAKE_INTERVAL = 8, -/** - * Max SPEC PSPOLL to be sent when the PSPOLL response has - * no-data bit set - */ -WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL = 9, + /** + * Max SPEC PSPOLL to be sent when the PSPOLL response has + * no-data bit set + */ + WMI_STA_PS_PARAM_QPOWER_SPEC_MAX_SPEC_NODATA_PSPOLL = 9, -/** - * Max value of ITO reset when there is no tx-rx - * after AP has set the TIM bit - */ -WMI_STA_PS_PARAM_MAX_RESET_ITO_COUNT_ON_TIM_NO_TXRX = 10, + /** + * Max value of ITO reset when there is no tx-rx + * after AP has set the TIM bit + */ + WMI_STA_PS_PARAM_MAX_RESET_ITO_COUNT_ON_TIM_NO_TXRX = 10, -/** - * Flag to enable/disable Powersave Optimization - * in WOW - */ -WMI_STA_PS_PARAM_ENABLE_PS_OPT_IN_WOW = 11, + /** + * Flag to enable/disable Powersave Optimization + * in WOW + */ + WMI_STA_PS_PARAM_ENABLE_PS_OPT_IN_WOW = 11, -/** - * Speculative interval in ms - */ -WMI_STA_PS_PARAM_SPEC_WAKE_INTERVAL = 12, + /** + * Speculative interval in ms + */ + WMI_STA_PS_PARAM_SPEC_WAKE_INTERVAL = 12, + /** + * Value determines the ITO level to apply + */ + WMI_STA_PS_PARAM_ITO_LEVEL = 13, }; typedef struct { @@ -20920,6 +21309,9 @@ typedef struct { wmi_mlo_flags mlo_flags; /* only mlo enable flag need by STA mode peer create */ } wmi_peer_create_mlo_params; +#define WMI_PEER_CREATE_GET_HW_PEER_ID_VALID(flags) WMI_GET_BITS(flags,0,1) +#define WMI_PEER_CREATE_SET_HW_PEER_ID_VALID(flags) WMI_SET_BITS(flags,0,1,value) + typedef struct { A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_create_cmd_fixed_param */ /** unique id identifying the VDEV, generated by the caller */ @@ -20928,7 +21320,19 @@ typedef struct { wmi_mac_addr peer_macaddr; /** peer type: see enum values above */ A_UINT32 peer_type; -/* The TLVs follows this structure: + /** Peer create flags */ + union { + struct { + A_UINT32 hw_peer_id_valid :1, + reserved :31; + }; + A_UINT32 flags; + }; + /** Global sw peer id, valid only if non-zero */ + A_UINT32 sw_peer_id; + /** Global hardware id, valid only if hw_peer_id_valid is set */ + A_UINT32 hw_peer_id; +/* The TLVs follow this structure: * wmi_peer_create_mlo_params mlo_params[]; <-- MLO flags on peer_create * Optional TLV, only present for MLO peers. * If the peer is non-MLO, the array length should be 0. @@ -21406,10 +21810,15 @@ typedef struct { #define WMI_PEER_PARAM_UL_OFDMA_RTD 0x2B /* - * Send unsolicited probe response to a connected STA. - * 0: Send immediately and stop. - * XX: Send every XX ms continuously. - * 0xFFFFFFFF: Stop sending immediately. + * Count and Interval to send unsolicited probe response to a connected STA. + * BIT 0-23 - Interval (in us) + * BIT 24-31 - Count (Number of probe response frame to send) + * + * Count : 0 - Stop sending immediately. + * Count : 1-254 - Send a probe response periodically at given interval + * until count expires. + * Count : 255 - Send a probe response periodically at given interval + * until stopped. */ #define WMI_PEER_PARAM_UNSOL_PROBE_RESP_INTVL 0x2C @@ -21621,13 +22030,14 @@ typedef struct { #define WMI_PEER_SAFEMODE_EN 0x80000000 /* Fips Mode Enabled */ /** define for peer_flags_ext */ -#define WMI_PEER_EXT_EHT 0x00000001 /* EHT enabled */ -#define WMI_PEER_EXT_320MHZ 0x00000002 /* 320Mhz enabled */ -#define WMI_PEER_EXT_DMS_CAPABLE 0x00000004 -#define WMI_PEER_EXT_HE_CAPS_6GHZ_VALID 0x00000008 /* param he_caps_6ghz is valid or not */ -#define WMI_PEER_EXT_IS_QUALCOMM_NODE 0x00000010 /* Indicates if the peer connecting is a qualcomm node */ -#define WMI_PEER_EXT_IS_MESH_NODE 0x00000020 /* Indicates if the peer connecting is a mesh node */ -#define WMI_PEER_EXT_PROTECTED_TWT 0x00000040 /* Protected TWT operation Support field in Extended RSN Capabilities element */ +#define WMI_PEER_EXT_EHT 0x00000001 /* EHT enabled */ +#define WMI_PEER_EXT_320MHZ 0x00000002 /* 320Mhz enabled */ +#define WMI_PEER_EXT_DMS_CAPABLE 0x00000004 +#define WMI_PEER_EXT_HE_CAPS_6GHZ_VALID 0x00000008 /* param he_caps_6ghz is valid or not */ +#define WMI_PEER_EXT_IS_QUALCOMM_NODE 0x00000010 /* Indicates if the peer connecting is a qualcomm node */ +#define WMI_PEER_EXT_IS_MESH_NODE 0x00000020 /* Indicates if the peer connecting is a mesh node */ +#define WMI_PEER_EXT_PROTECTED_TWT 0x00000040 /* Protected TWT operation Support field in Extended RSN Capabilities element */ +#define WMI_PEER_EXT_UHR 0x00000080 /* UHR enabled */ #define WMI_PEER_EXT_F_CRIT_PROTO_HINT_ENABLED 0x40000000 /** @@ -21802,6 +22212,120 @@ typedef struct { wmi_mac_addr link_macaddr; } wmi_pdev_mesh_rx_filter_enable_fixed_param; +typedef struct { + A_UINT32 tlv_header; /** TLV tag (WMITLV_TAG_STRUC_wmi_peer_assoc_operating_mode_params) and len */ + /* rx_nss: + * self rx nss indicated to AP through capability IEs or operating mode IE. + * 1 ~ n: 1ss ~ nss + */ + A_UINT32 rx_nss; + /* tx_nss: + * self tx nss indicated to AP through capability IEs or operating mode IE. + * 1 ~ n: 1ss ~ nss + */ + A_UINT32 tx_nss; + /* bw: + * self BW indicated to AP through capability IEs or operating mode IE, + * refer to wmi_channel_width for definition of the values this field + * can hold. + */ + A_UINT32 bw; +} wmi_peer_assoc_operating_mode_params; + + +typedef enum { + WMI_MGMT_TID_MSDUQ_LINK_SPECIFIC,/* legacy and link peer */ + WMI_MGMT_TID_MSDUQ_LINK_AGNOSTIC, + WMI_MGMT_TID_MSDUQ_TYPE_MAX, +} WMI_MGMT_TID_MSDUQ_TYPE; + +#define WMI_MGMT_MSDUQ_GET_LINK_ID(msduq_type) WMI_GET_BITS(msduq_type,0,3) +#define WMI_MGMT_MSDUQ_SET_LINK_ID(msduq_type) WMI_SET_BITS(msduq_type,0,3,value) +#define WMI_MGMT_MSDUQ_GET_FLOW_TYPE(msduq_type) WMI_GET_BITS(msduq_type,3,5) +#define WMI_MGMT_MSDUQ_SET_FLOW_TYPE(msduq_type) WMI_SET_BITS(msduq_type,3,5,value) + +typedef struct { + A_UINT32 tlv_header; /* TLV tag and Len. Tag: WMITLV_TAG_STRUC_wmi_peer_assoc_mgmt_msduq_params*/ + union { + struct { + A_UINT32 link_id : 3, /* HW Link ID */ + flow_type : 5, /* WMI_MGMT_TID_MSDUQ_TYPE */ + reserved : 24; + }; + A_UINT32 msduq_type; + }; + + /* 40 bit physical address, 256 bytes alligned, LSB 8 bits are zero */ + A_UINT32 mgmt_msduq_paddr_39_8; +} wmi_peer_assoc_mgmt_msduq_params; + +typedef struct { + A_UINT32 tlv_header; /* TAG_ID : WMITLV_TAG_STRUC_wmi_peer_assoc_mgmt_mpduq_params */ + /* 40 bit physical address, 256 bytes alligned, LSB 8 bits are zero */ + A_UINT32 mgmt_mpduq_paddr_39_8; + A_UINT32 pn_paddr_31_0; + A_UINT32 pn_paddr_39_32; +} wmi_peer_assoc_mgmt_mpduq_params; + +#define WMI_HOL_QUEUE_GET_PEER_ID(mpduq_msduq_number) \ + WMI_GET_BITS(mpduq_msduq_number,0,12) +#define WMI_HOL_QUEUE_SET_PEER_ID(mpduq_msduq_number) \ + WMI_SET_BITS(mpduq_msduq_number,0,12,value) +#define WMI_HOL_QUEUE_GET_TID_NUM(mpduq_msduq_number) \ + WMI_GET_BITS(mpduq_msduq_number,12,5) +#define WMI_HOL_QUEUE_SET_TID_NUM(mpduq_msduq_number) \ + WMI_SET_BITS(mpduq_msduq_number,12,5,value) +#define WMI_HOL_QUEUE_GET_MPDUQ_TYPE(mpduq_msduq_number) \ + WMI_GET_BITS(mpduq_msduq_number,17,5) +#define WMI_HOL_MSDUQ_SET_MPDUQ_TYPE(mpduq_msduq_number) \ + WMI_SET_BITS(mpduq_msduq_number,17,5,value) +#define WMI_HOL_QUEUE_GET_MSDUQ_TYPE(mpduq_msduq_number) \ + WMI_GET_BITS(mpduq_msduq_number,22,5) +#define WMI_HOL_MSDUQ_SET_MSDUQ_TYPE(mpduq_msduq_number) \ + WMI_SET_BITS(mpduq_msduq_number,22,5,value) + +typedef struct { + A_UINT32 tlv_header; /* TAG_ID : WMITLV_TAG_STRUC_wmi_peer_assoc_hol_mdsuq_params */ + + /** + * A_UINT32 + * WMI_HOL_MSDUQ_GET_PEER_ID / WMI_HOL_MSDU_SET_PEER_ID + * peer_id:12, + * + * WMI_HOL_MSDUQ_GET_TID_NUM / WMI_HOL_MSDUQ_SET_TID_NUM + * tid_num:5, + * + * WMI_HOL_MPDUQ_GET_QUEUE_TYPE / WMI_HOL_MPDUQ_SET_QUEUE_TYPE + * mpduq_type:5, + * + * WMI_HOL_MSDUQ_GET_QUEUE_TYPE / WMI_HOL_MSDUQ_SET_QUEUE_TYPE + * msduq_type:5, + * + * rsvd:5; + */ + union { + struct { + A_UINT32 peer_id : 12, + tid_num : 5, + mpduq_type : 5, + msduq_type : 5, + reserved : 5; + }; + A_UINT32 mpduq_msduq_number; + }; + + /* 40 bit address, 256 bytes alligned LSB 8 bits are zero */ + A_UINT32 mpduq_paddr_39_8; + + /* 40 bit address, 256 bytes alligned LSB 8 bits are zero */ + A_UINT32 msduq_paddr_39_8; + + /* First 32 bits for pn physical address */ + A_UINT32 pn_paddr_31_0; + + /* Upper 8 bits of 40 bit pn physical address */ + A_UINT32 pn_paddr_39_32; +} wmi_peer_assoc_hol_mdsuq_params; /* * PEER assoc_flags for assoc complete: @@ -21943,6 +22467,13 @@ typedef struct { A_UINT32 peer_eht_ops; wmi_ppe_threshold peer_eht_ppet; A_UINT32 assoc_flags; + /** maximum number of spatial streams supported by peer for tx */ + A_UINT32 peer_max_tx_nss; + /* max_downlink_nss: + * max downlink nss, intersected between self rx and peer tx. + * 1~N means 1ss~Nss + */ + A_UINT32 max_downlink_nss; /* Following this struct are the TLV's: * A_UINT8 peer_legacy_rates[]; @@ -21955,6 +22486,8 @@ typedef struct { * wmi_eht_rate_set peer_eht_rates; <-- EHT capabilities of the peer * wmi_peer_assoc_mlo_partner_link_params link_info[] <-- partner link info * wmi_peer_assoc_tid_to_link_map[] <-- tid to link_map info + * wmi_peer_assoc_operating_mode_params <-- operating mode param that + * host sends to AP in peer assoc req, optional TLV */ } wmi_peer_assoc_complete_cmd_fixed_param; @@ -22503,14 +23036,19 @@ typedef struct { /** * btm_config.flags * BIT 0 : Enable/Disable the BTM offload. - * BIT 1-2 : Action on non matching candidate with cache. Used WMI_ROAM_BTM_OFLD_NON_MATCHING_CND_XXX + * BIT 1-2 : Action on non matching candidate with cache. + * Used WMI_ROAM_BTM_OFLD_NON_MATCHING_CND_XXX * BIT 3-5 : Roaming handoff decisions. Use WMI_ROAM_BTM_OFLD_CNDS_MATCH_XXX * BIT 6 : Enable/Disable solicited BTM - * BIT 7 : Roam BTM candidates based on the roam score instead of BTM preferred value + * BIT 7 : Roam BTM candidates based on the roam score instead of BTM + * preferred value * BIT 8 : BTM query preference over 11k neighbor report request * BIT 9 : Send BTM query with preferred candidates list - * BIT 10 : Forward MBO BTM Request to Host if MBO ASSOC RETRY attribute is set - * BIT 11-31 : Reserved + * BIT 10 : Forward MBO BTM Request to Host if MBO ASSOC RETRY attribute + * is set + * BIT 11 : Detect the missing band from BTM request when compared to + * roam scan results and consider them as valid candidate + * BIT 12-31 : Reserved */ #define WMI_ROAM_BTM_SET_ENABLE(flags, val) WMI_SET_BITS(flags, 0, 1, val) #define WMI_ROAM_BTM_GET_ENABLE(flags) WMI_GET_BITS(flags, 0, 1) @@ -22526,8 +23064,10 @@ typedef struct { #define WMI_ROAM_BTM_GET_BTM_QUERY_PREFERENCE_OVER_11K(flags) WMI_GET_BITS(flags, 8, 1) #define WMI_ROAM_BTM_SET_BTM_QUERY_WITH_CANDIDATE_LIST(flags, val) WMI_SET_BITS(flags, 9, 1, val) #define WMI_ROAM_BTM_GET_BTM_QUERY_WITH_CANDIDATE_LIST(flags) WMI_GET_BITS(flags, 9, 1) -#define WMI_ROAM_BTM_SET_FORWARD_MBO_ASSOC_RETRY_BTM_REQUEST_TO_HOST(flags, val) WMI_SET_BITS(flags, 10, 1, val) -#define WMI_ROAM_BTM_GET_FORWARD_MBO_ASSOC_RETRY_BTM_REQUEST_TO_HOST(flags) WMI_GET_BITS(flags, 10, 1) +#define WMI_ROAM_BTM_SET_FORWARD_MBO_ASSOC_RETRY_BTM_REQUEST_TO_HOST(flags, val) WMI_SET_BITS(flags, 10, 1, val) +#define WMI_ROAM_BTM_GET_FORWARD_MBO_ASSOC_RETRY_BTM_REQUEST_TO_HOST(flags) WMI_GET_BITS(flags, 10, 1) +#define WMI_ROAM_BTM_SET_DETECT_CANDIDATE_FROM_MISSING_BAND_IN_BTM_REQUEST(flags, val) WMI_SET_BITS(flags, 11, 1, val) +#define WMI_ROAM_BTM_GET_DETECT_CANDIDATE_FROM_MISSING_BAND_IN_BTM_REQUEST(flags) WMI_GET_BITS(flags, 11, 1) /** WMI_ROAM_BTM_SET_NON_MATCHING_CNDS_ACTION definition: When BTM candidate is not matched with cache by WMI_ROAM_BTM_SET_CNDS_MATCH_CONDITION, determine what to do */ @@ -22625,6 +23165,15 @@ typedef struct { A_UINT32 roam_scan_period_after_inactivity; /* units = milliseconds */ /** roam full scan period value */ A_UINT32 roam_full_scan_period; /* units = milliseconds */ + /** roam_periodic_scan_interval: + * Timer value to periodically trigger the roaming process at + * set intervals during low RSSI roaming trigger. + * Low rssi trigger (Partial/full) --> + * 10s (partial) --> + * 20s (partial) --> + * 30s (partial) and so on. + */ + A_UINT32 roam_periodic_scan_interval; /* units = seconds */ } wmi_roam_scan_period_fixed_param; /** @@ -23177,7 +23726,10 @@ typedef struct { * Refer WLAN_ROAM_SCORE_MAX_BAND_INDEX for possible band_idx values. */ A_UINT32 band_idx; - /** Below RSSI/CU factor_value & factor_score param values are configured by vendor */ + /** + * The below band weight (2.4 GHz, 5 GHz, 6 GHz), RSSI, and CU factor_value + * and factor_score param values are configured by vendor. + */ A_UINT32 rssi_factor_value1; A_UINT32 rssi_factor_value2; A_UINT32 rssi_factor_value3; @@ -23192,6 +23744,9 @@ typedef struct { A_UINT32 cu_factor_value2; A_UINT32 cu_factor_score1; A_UINT32 cu_factor_score2; + A_UINT32 band_weight_2GHz; + A_UINT32 band_weight_5GHz; + A_UINT32 band_weight_6GHz; } wmi_roam_cnd_vendor_scoring_param; /** Support early stop roaming scanning when finding a strong candidate AP @@ -23445,6 +24000,7 @@ typedef struct { A_UINT32 no_ack_timeout; /* In msec. duration to wait before another SW retry made if no ack seen for previous frame */ A_UINT32 roam_candidate_validity_time; /* In msec. validity duration of each entry in roam cache. If the value is 0x0, this field should be disregarded. */ A_UINT32 roam_to_current_bss_disable; /* Disable roaming to current bss */ + A_UINT32 mlo_roam_partner_bringup_by_host; } wmi_roam_offload_tlv_param; @@ -23673,6 +24229,21 @@ typedef struct { wmi_mac_addr mac_addr; } wmi_roam_bss_info_param; +typedef struct { + A_UINT32 tlv_header; + /* These params are filled in the new design done for MLO roam + * optimization; only in roam abort cases Fw deletes partner links + * at the start of roam handoff itself. + * Host needs to take care of bringing up the partner link if + * roam fails based on deleted_ieee_link_id_bmap; + * deleted_ieee_link_id_bmap represents the ieee_link_id of the + * links which were deleted at the start of roam handoff. + * This is filled only for MLO and deleted_ieee_link_id_bmap = 0 + * means no link was deleted. + */ + A_UINT32 deleted_ieee_link_id_bmap; +} wmi_roam_partner_link_param; + /* roam_reason: bits 0-3 */ #define WMI_ROAM_REASON_INVALID 0x0 /** invalid reason. Do not interpret reason field */ #define WMI_ROAM_REASON_BETTER_AP 0x1 /** found a better AP */ @@ -24577,6 +25148,12 @@ typedef enum wake_reason_e { WOW_REASON_PF_BLOCKING_LAST_TIME, /* C2C scan report LPI AP detect or not event */ WOW_REASON_C2C_DETECT_EVENT, + /* wake up the host in case of TDLS packet reception */ + WOW_REASON_TDLS_PACKET_RX, + /* wake up the host when USD is enabled */ + WOW_REASON_USD, + /* wake up the host when MLO link switch happens */ + WOW_REASON_MLO_LINK_SWITCH_EVENT, /* add new WOW_REASON_ defs before this line */ @@ -26282,6 +26859,11 @@ typedef enum */ WMI_VENDOR_OUI_ACTION_AUTH_ASSOC_6MBPS_2GHZ = 17, + /* + * Disable dynamic SMPS if OUI matches + */ + WMI_VENDOR_OUI_ACTION_DISABLE_DYNAMIC_SMPS = 18, + /* Add any action before this line */ WMI_VENDOR_OUI_ACTION_MAX_ACTION_ID @@ -27170,13 +27752,32 @@ typedef struct { typedef struct { /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_tdls_peer_update_cmd_fixed_param */ - A_UINT32 tlv_header; + A_UINT32 tlv_header; /** unique id identifying the VDEV */ - A_UINT32 vdev_id; + A_UINT32 vdev_id; /** peer MAC address */ - wmi_mac_addr peer_macaddr; + wmi_mac_addr peer_macaddr; /** new TDLS state for peer (wmi_tdls_peer_state) */ - A_UINT32 peer_state; + A_UINT32 peer_state; + /** need_nss_conf: + * only set to 1 when this TDLS peer is required to operating + * in particular HW mode NSS + */ + A_UINT32 need_nss_conf; + /* preferred_tx_nss: + * for peering state, it means advertised nss, + * for connected state, it means final negotiated nss. + * 1 ~ n: 1ss ~ nss + */ + A_UINT32 preferred_tx_nss; + /* preferred_rx_nss: + * for peering state, it means advertised nss, + * for connected state, it means final negotiated nss. + * 1 ~ n: 1ss ~ nss + */ + A_UINT32 preferred_rx_nss; + + /* The TLV for wmi_tdls_peer_capabilities will follow. * wmi_tdls_peer_capabilities peer_caps; */ @@ -27238,6 +27839,8 @@ enum wmi_tdls_peer_notification { WMI_TDLS_PEER_DISCONNECTED, /** TDLS/BT role change notification for connection tracker */ WMI_TDLS_CONNECTION_TRACKER_NOTIFICATION, + /** resp to WMI_TDLS_PEER_UPDATE_CMDID as host requested */ + WMI_TDLS_PEER_UPDATE_RESP, }; enum wmi_tdls_peer_reason { @@ -27267,6 +27870,8 @@ enum wmi_tdls_peer_reason { WMI_TDLS_SCAN_STARTED_EVENT, /** TDLS module received a scan complete event, TDLS connection tracker needs to handle this */ WMI_TDLS_SCAN_COMPLETED_EVENT, + /** TDLS max supported operating NSS in current HW mode */ + WMI_TDLS_OPERATING_NSS_CONF_EVENT, }; /* WMI_TDLS_PEER_EVENTID */ @@ -27281,6 +27886,10 @@ typedef struct { A_UINT32 peer_reason; /** unique id identifying the VDEV */ A_UINT32 vdev_id; + /** operating TX NSS 1 ~ n: 1ss ~ nss */ + A_UINT32 tx_nss; + /** operating RX NSS 1 ~ n: 1ss ~ nss */ + A_UINT32 rx_nss; } wmi_tdls_peer_event_fixed_param; /* NOTE: wmi_vdev_mcc_bcn_intvl_change_event_fixed_param would be deprecated. Please @@ -27759,8 +28368,8 @@ typedef struct #define LPI_IE_BITMAP_CHRE_RADIO_CHAIN 0x01000000 /* include radio chain and RSSI per chain information if this bit is set - for CHRE */ #define LPI_IE_BITMAP_CHRE_SEC_MODE_MRSNO_WIFI6 0x02000000 /* include MRSNO IE's sec_mode information for WiFi6 if this bit is set - for CHRE */ #define LPI_IE_BITMAP_CHRE_SEC_MODE_MRSNO_WIFI7 0x04000000 /* include MRSNO IE's sec_mode information for WiFi7 if this bit is set - for CHRE */ +#define LPI_IE_BITMAP_INTERWORKING_IE_VENUE_INFO 0x08000000 /* interworking IE venue info (2 bytes) will be filled when this bit is enabled */ -/* 0x08000000 is unused / available */ #define LPI_IE_BITMAP_CHRE_ESS 0x10000000 /* ESS capability info for CHRE */ #define LPI_IE_BITMAP_CHRE_SEC_MODE 0x20000000 /* Security capability info for CHRE */ @@ -28460,6 +29069,12 @@ typedef enum { WMI_PEER_IND_OMI, /* operating mode indication */ } WMI_PEER_OPER_MODE_IND; + +#define WMI_EHT_PEER_PARAMS_MCS_DISABLE_GET(eht_peer_params) \ + WMI_GET_BITS(eht_peer_params, 0, 1) +#define WMI_EHT_PEER_PARAMS_MCS_DISABLE_SET(eht_peer_params, value) \ + WMI_SET_BITS(eht_peer_params, 0, 1, value) + typedef struct { /** TLV tag and len; tag equals * WMITLV_TAG_STRUC_wmi_peer_oper_mode_change */ @@ -28481,6 +29096,12 @@ typedef struct { * valid for peer_operating mode ind. OMI */ A_UINT32 new_disablemu; + /** eht_peer_params + * bit 0 - eht_mcs15_disable, refer to + * WMI_EHT_PEER_PARAMS_MCS_DISABLE_GET,SET macros + * bits 1 to 31 - reserved + */ + A_UINT32 eht_peer_params; } wmi_peer_oper_mode_change_event_fixed_param; /** FW response when tx failure count has reached threshold @@ -29479,6 +30100,21 @@ typedef struct { #define wmi_ndp_cmd_param wmi_ndp_cmd_param_PROTOTYPE +typedef struct { + /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_ndp_set_latency_tput_fixed_param */ + A_UINT32 tlv_header; + /** NDP instance id */ + A_UINT32 ndp_instance_id; + /** NDI VDEV ID */ + A_UINT32 vdev_id; + /** latency reqirement */ + A_UINT32 latency_ms; + /** throughput requirement */ + A_UINT32 tput_mbps; +} wmi_ndp_set_latency_tput_fixed_param_PROTOTYPE; + +#define wmi_ndp_set_latency_tput_fixed_param wmi_ndp_set_latency_tput_fixed_param_PROTOTYPE + /** * NDP End request */ @@ -31306,6 +31942,11 @@ typedef struct { A_UINT32 pdev_id; } wmi_pdev_resume_event_fixed_param; +/** WMI_PDEV_SUSPEND_EVENTID: generated in response to WMI_PDEV_SUSPEND_CMDID */ +typedef struct { + A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_suspend_event_fixed_param */ + A_UINT32 pdev_id; +} wmi_pdev_suspend_event_fixed_param; /** value representing all modules */ @@ -33236,6 +33877,23 @@ typedef struct { A_UINT32 tid_mask; /* bits 0 to 15 = QoS TIDs, bit 16 = non-qos TID */ } wmi_peer_reorder_queue_remove_cmd_fixed_param; +/** + * This command is sent from WLAN host driver to firmware for + * customizing MCS range & retry count for specific TID of specific peer + */ +typedef struct { + A_UINT32 tlv_header; + A_UINT32 vdev_id; + wmi_mac_addr peer_macaddr; /* Peer MAC address */ + A_UINT32 tid; + /* on_off: + * Rate customization enable/disable. 1 for enable and 0 for disable. + */ + A_UINT32 on_off; + A_UINT32 bw; /* Unit MHz */ + A_UINT32 retry_count; +} wmi_peer_tid_rate_custom_cmd_fixed_param; + /* DEPRECATED - use wmi_pdev_set_mac_config_response_event_fixed_param instead */ typedef struct { @@ -33887,6 +34545,81 @@ typedef struct wmi_bpf_get_vdev_work_memory_resp_evt_s { */ } wmi_bpf_get_vdev_work_memory_resp_evt_fixed_param; + +/* APF offloads supported bitmap: + * + * BIT 0: ARP OFFLOAD + * BIT 1: NS OFFLOAD + * BIT 2: IGMP OFFLOAD + * BIT 3: ICMP OFFLOAD + * BIT 4-31: reserved +*/ +#define WMI_BPF_ARP_OFFLOAD_SUPPORT_GET(param) \ + WMI_GET_BITS(param, 0, 1) +#define WMI_BPF_ARP_OFFLOAD_SUPPORT_SET(param, value) \ + WMI_SET_BITS(param, 0, 1, value) + +#define WMI_BPF_NS_OFFLOAD_SUPPORT_GET(param) \ + WMI_GET_BITS(param, 1, 1) +#define WMI_BPF_NS_OFFLOAD_SUPPORT_SET(param, value) \ + WMI_SET_BITS(param, 1, 1, value) + +#define WMI_BPF_IGMP_OFFLOAD_SUPPORT_GET(param) \ + WMI_GET_BITS(param, 2, 1) +#define WMI_BPF_IGMP_OFFLOAD_SUPPORT_SET(param, value) \ + WMI_SET_BITS(param, 2, 1, value) + +#define WMI_BPF_ICMP_OFFLOAD_SUPPORT_GET(param) \ + WMI_GET_BITS(param, 3, 1) +#define WMI_BPF_ICMP_OFFLOAD_SUPPORT_SET(param, value) \ + WMI_SET_BITS(param, 3, 1, value) + +typedef struct wmi_bpf_set_supported_offload_bitmap_cmd_s { + A_UINT32 tlv_header; + A_UINT32 vdev_id; + /* ofld_bitmap: + * Host sends bitmap for APF supported offloads. + * Refer to the above WMI_BPF_ macros for the interpretation of the bits + * within the bitmap. + */ + A_UINT32 ofld_bitmap; +} wmi_bpf_set_supported_offload_bitmap_cmd_fixed_param; + +/* APF modes: */ +typedef enum { + /* Default value: 0 */ + wmi_apf_mode_default = 0, + + /* Mode 1: value 1: APF to operate only during system suspend. */ + wmi_apf_mode_system_suspend = 1, + + /* Mode 2: value 2: + * Downgrade the APF capability of the firmware to a lower version + * (from V6 to V4). + */ + wmi_apf_mode_capability_v4 = 2, + + /* Mode combination: value 3: + * Downgrade to APFv4 and enable only in system suspend. + */ + wmi_apf_mode_system_suspend_and_capability_v4 = 3, + + /* Mode 3: value 4: Turn off APF completely. */ + wmi_apf_mode_off = 4, +} wmi_apf_modes; + +typedef struct wmi_bpf_set_apf_mode_cmd_s { + A_UINT32 tlv_header; /* tag = WMITLV_TAG_STRUC_wmi_bpf_set_apf_mode_cmd_fixed_param */ + A_UINT32 vdev_id; + /* apf_mode: + * Host indicates the APF mode (or combination of modes). + * Refer to the wmi_apf_modes enum for the interpretation of the + * apf_mode value. + */ + A_UINT32 apf_mode; /* holds a wmi_apf_modes value */ +} wmi_bpf_set_apf_mode_cmd_fixed_param; + + #define AES_BLOCK_LEN 16 /* in bytes */ #define FIPS_KEY_LENGTH_128 16 /* in bytes */ #define FIPS_KEY_LENGTH_256 32 /* in bytes */ @@ -34406,6 +35139,41 @@ typedef enum { * A_INT8 ICNIRP 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) * A_INT8 ICNIRP 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) * A_INT8 ICNIRP 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) + * if version == 2 or chip is col, will have NONTAS POWER LIMIT + * ====================NONTAS POWER LIMIT VALUE====================== + * A_INT8 NONTAS 2 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) + * A_INT8 NONTAS 2 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) + * A_INT8 NONTAS 2 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) + * A_INT8 NONTAS 5 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-1, Ch32 ~ Ch48) + * A_INT8 NONTAS 5 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-1, Ch32 ~ Ch48) + * A_INT8 NONTAS 5 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-1, Ch32 ~ Ch48) + * A_INT8 NONTAS 5 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-2, Ch50 ~ Ch144) + * A_INT8 NONTAS 5 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-2, Ch50 ~ Ch144) + * A_INT8 NONTAS 5 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-2, Ch50 ~ Ch144) + * A_INT8 NONTAS 5 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-3, Ch149 ~ Ch161) + * A_INT8 NONTAS 5 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-3, Ch149 ~ Ch161) + * A_INT8 NONTAS 5 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-3, Ch149 ~ Ch161) + * A_INT8 NONTAS 5 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-4, Ch163 ~ Ch177) + * A_INT8 NONTAS 5 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-4, Ch163 ~ Ch177) + * A_INT8 NONTAS 5 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-4, Ch163 ~ Ch177) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch1, Ch2 ~ Ch41) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch1, Ch2 ~ Ch41) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch1, Ch2 ~ Ch41) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch45 ~ Ch93) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch45 ~ Ch93) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-5) (Ch45 ~ Ch93) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-6) (Ch97~ Ch113) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-6) (Ch97~ Ch113) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-6) (Ch97~ Ch113) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-7) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz SISO (Chain0) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz SISO (Chain1) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) + * A_INT8 NONTAS 6 GHz MIMO (Chain0 + Chain1) Power Limit Value(unit: 0.25dBm) (UNII-8) (Ch117~Ch149) */ BIOS_PARAM_TYPE_BANDEDGE_CTL_POWER, @@ -34578,6 +35346,21 @@ typedef enum { * ============================================================== */ + BIOS_PARAM_TYPE_NON_SAR_COUNTRY_CONFIG, + /* + * BIOS_PARAM_TYPE_NON_SAR_COUNTRY_CONFIG structure contains 256 bytes as below + * + * A_UINT8 country_bitmap[NON_SAR_COUNTRY_BITMAP_COUNT];//NON_SAR_COUNTRY_BITMAP_COUNT = 256, 256BYTE. + * 0-255 stand for 256 country, each country has 8 bit for configuration. + * + * ==================each bit configuration=========================== + * BIT0: Skip TAS limit config + * BIT1: Skip SAR limit config + * BIT2: Use domain GEO table. 1: Use Country GEO table + * BIT3~5: index of domain/country used which group GEO offset table + * BIT6~7: reserved + * ==================================================================== + */ BIOS_PARAM_TYPE_MAX, } bios_param_type_e; @@ -36463,6 +37246,7 @@ typedef enum { WMI_REQUEST_CTRL_PATH_PDEV_BCN_TX_STAT = 20, WMI_REQUEST_CTRL_PATH_PDEV_CONN_STAT = 21, WMI_REQUEST_CTRL_PATH_ML_RECONFIG_STAT = 22, + WMI_REQUEST_CTRL_PATH_STA_DAR_STAT = 23, } wmi_ctrl_path_stats_id; typedef enum { @@ -36482,6 +37266,16 @@ typedef enum { WMI_REQUEST_CTRL_PATH_STAT_PERIODIC_PUBLISH = 5, } wmi_ctrl_path_stats_action; +typedef enum { + /* + * The following stats actions are mutually exclusive. + * A single stats request message can only specify one action. + */ + WMI_REQUEST_CTRL_PATH_STAT_DEFAULT = 0, /* unspecified granularity */ + WMI_REQUEST_CTRL_PATH_STAT_AC_LEVEL = 1, + WMI_REQUEST_CTRL_PATH_STAT_TID_LEVEL = 2, +} wmi_ctrl_path_stats_granularity; + typedef enum { WMI_HALPHY_CTRL_PATH_SU_STATS = 0, WMI_HALPHY_CTRL_PATH_SUTXBF_STATS, @@ -36518,6 +37312,13 @@ typedef struct { */ A_UINT32 stat_periodicity; + /** stats_granularity: + * Configures AC vs. TID granularity stats reporting, + * e.g. for STA_DAR_STATs. + * Possible values are listed in the wmi_ctrl_path_stats_granularity enum. + */ + A_UINT32 stats_granularity; /* refer to wmi_ctrl_path_stats_granularity */ + /** The below TLV arrays optionally follow this fixed_param TLV structure: * 1. A_UINT32 pdev_ids[]; * If this array is present and non-zero length, stats should only @@ -37011,8 +37812,9 @@ typedef struct { supports_11ac:1, supports_11ax:1, supports_11be:1, + supports_11bn:1, - unused: 21, + unused: 20, max_mubfee: 4; /* max MU beamformees supported per MAC */ }; @@ -37493,6 +38295,7 @@ typedef struct { */ typedef enum { WMI_TWT_STA_SYNC_EVENT_CAP = 1, /* STA TWT: FW internal errors reported using sync WMI_TWT_ACK_EVENTID */ + WMI_TWT_FLEXI_SUPPORT = 2, /* Add new TWT Caps above */ WMI_TWT_MAX_CAP = 32, @@ -38705,6 +39508,17 @@ static INLINE A_UINT8 *wmi_id_to_name(A_UINT32 wmi_command) WMI_RETURN_STRING(WMI_MLO_LINK_RECONFIG_COMPLETE_CMDID); WMI_RETURN_STRING(WMI_SAWF_EZMESH_HOP_COUNT_CMDID); WMI_RETURN_STRING(WMI_VDEV_VBSS_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_NDP_SET_LATENCY_TPUT_CMDID); + WMI_RETURN_STRING(WMI_MLO_LINK_TTLM_COMPLETE_CMDID); + WMI_RETURN_STRING(WMI_BPF_SET_SUPPORTED_OFFLOAD_BITMAP_CMDID); + WMI_RETURN_STRING(WMI_BPF_SET_APF_MODE_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_PCIE_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_PCIE_LPM_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_DCVS_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_EDPS_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_PUO_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_ENERGY_MGMT_ECO_MODE_CONFIG_CMDID); + WMI_RETURN_STRING(WMI_PEER_TID_RATE_CUSTOM_CMDID); } return (A_UINT8 *) "Invalid WMI cmd"; @@ -39126,9 +39940,10 @@ typedef struct { A_UINT32 num_6g_reg_rules_client_lpi[WMI_REG_CLIENT_MAX]; A_UINT32 num_6g_reg_rules_client_vlp[WMI_REG_CLIENT_MAX]; /* - * NOTE: no further fields can be added into this struct, due to - * message buffer size limitations in certain targets for the - * WMI_REG_CHAN_LIST_CC_EXT_EVENT message. + * NOTE: We cannot add new parameters to the fixed param TLV though + * we have enough buffer size (WMI_SVC_MSG_SIZE) for the given WMI event. + * This is due to a crash seen in the host parsing logic of this fixed param. + * New params can be added in the same message but in other TLVs. */ /* * This fixed_param TLV is followed by the following TLVs: @@ -39144,6 +39959,10 @@ typedef struct { * - wmi_regulatory_rule_meta_data reg_meta_data[] * struct used to fill meta information specific to new reg rules * getting added(i.e. from C2C onwards). + * - wmi_hw_blacklist_chan_fixed_param hw_blacklist_chan_fixed_param[0 or 1] + * optional TLV for reporting HW channel blacklist meta-data. + * - wmi_hw_blacklist_chan_data, hw_blacklist_chan_data[] + * optional TLV for reporting HW channel blacklist information. */ } wmi_reg_chan_list_cc_event_ext_fixed_param; @@ -39296,6 +40115,10 @@ typedef struct { * This TLV array contains zero or more TLVs of channel CFI and * EIRP power values for each of the total number of channels * per global operating class. + * 6. wmi_hw_blacklist_chan_fixed_param hw_blacklist_chan_fixed_param[] + * optional meta-data for HW channel blacklist + * 7. wmi_hw_blacklist_chan_data hw_blacklist_chan_data[] + * optional HW channel blacklist information */ } wmi_afc_event_fixed_param; @@ -39388,6 +40211,233 @@ typedef struct { A_UINT32 eirp_pwr; /* maximum permissible EIRP available for above CFI in dBm, value is stored in 0.01 dBm steps */ } wmi_afc_chan_eirp_power_info; + +typedef enum { + WMI_11BE_PUNCTURE_PATTERN_80MHZ_MINUS_20MHZ_0x1 = 0, + WMI_11BE_PUNCTURE_PATTERN_80MHZ_MINUS_20MHZ_0x2 = 1, + WMI_11BE_PUNCTURE_PATTERN_80MHZ_MINUS_20MHZ_0x4 = 2, + WMI_11BE_PUNCTURE_PATTERN_80MHZ_MINUS_20MHZ_0x8 = 3, + WMI_11BE_PUNCTURE_PATTERN_80MHZ_MAX, +} WMI_11BE_PUNCTURE_PATTERNS_80MHZ; + +typedef enum { + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x1 = 0, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x2 = 1, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x4 = 2, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x8 = 3, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x10 = 4, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x20 = 5, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x40 = 6, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_20MHZ_0x80 = 7, + + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_40MHZ_0x0C = 8, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_40MHZ_0x03 = 9, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_40MHZ_0xC0 = 10, + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MINUS_40MHZ_0x30 = 11, + + WMI_11BE_PUNCTURE_PATTERN_160MHZ_MAX, +} WMI_11BE_PUNCTURE_PATTERNS_160MHZ; + +typedef enum { + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0xC = 0, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0x3 = 1, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0xC0 = 2, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0x30 = 3, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0xC00 = 4, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0x300 = 5, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0xC000 = 6, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_40MHZ_0x3000 = 7, + + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_80MHZ_0xF = 8, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_80MHZ_0xF0 = 9, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_80MHZ_0xF00 = 10, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_80MHZ_0xF000 = 11, + + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xF003 = 12, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xF00C = 13, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xF030 = 14, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xF0C0 = 15, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xF300 = 16, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xFC00 = 17, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0x003F = 18, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0x00CF = 19, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0x030F = 20, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0x0C0F = 21, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0x300F = 22, + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MINUS_120MHZ_0xC00F = 23, + + WMI_11BE_PUNCTURE_PATTERN_320MHZ_MAX, +} WMI_11BE_PUNCTURE_PATTERNS_320MHZ; + +typedef enum { + /* + * bits 16-19 of "blacklist_msg_info" in wmi_hw_blacklist_chan_fixed_param. + * This status code intimates host whether the TLV sent by + * Halphy is to update the master channel list or clear it. + */ + WMI_HW_BLACKLIST_CHAN_SP_UPDATE = 0, + WMI_HW_BLACKLIST_CHAN_VLP_UPDATE, + /* the below enums are added for future scope */ + WMI_HW_BLACKLIST_CHAN_SP_CLEAR, + WMI_HW_BLACKLIST_CHAN_VLP_CLEAR, + WMI_HW_BLACKLIST_CHAN_UPDATE_ALL, + WMI_HW_BLACKLIST_CHAN_CLEAR_ALL, + WMI_HW_BLACKLIST_CHAN_INVALID = 15, +} WMI_HW_BLACKLIST_CHAN_RESP_CMD_CODE; + +typedef enum { + /* + * bit 20, "blacklist_msg_info" flag in wmi_hw_blacklist_chan_fixed_param + * This bit field informs the host whether all the blacklist channel + * information has been sent, or if further messages will follow to + * deliver the remaining information. + */ + WMI_HW_BLACKLIST_CHAN_EVENT_DONE = 0, /* Indicates it is the last event */ + WMI_HW_BLACKLIST_CHAN_EVENT_MORE = 1, /* Indicates more evts will follow */ +} WMI_HW_BLACKLIST_CHAN_DATA_EVENT_FLAG; + +#define WMI_GET_BLACKLIST_MSG_INFO_WMI_EVT_SEQ_NUM(flag) \ + WMI_GET_BITS(flag, 0, 8) +#define WMI_SET_BLACKLIST_MSG_INFO_WMI_EVT_SEQ_NUM(flag, val) \ + WMI_SET_BITS(flag, 0, 8, val) + +#define WMI_GET_BLACKLIST_MSG_INFO_TOTAL_WMI_EVT_NUM(flag) \ + WMI_GET_BITS(flag, 8, 8) +#define WMI_SET_BLACKLIST_MSG_INFO_TOTAL_WMI_EVT_NUM(flag, val) \ + WMI_SET_BITS(flag, 8, 8, val) + +#define WMI_GET_BLACKLIST_MSG_INFO_RESP_CODE(flag) \ + WMI_GET_BITS(flag, 16, 4) +#define WMI_SET_BLACKLIST_MSG_INFO_RESP_CODE(flag, val) \ + WMI_SET_BITS(flag, 16, 4, val) + +#define WMI_GET_BLACKLIST_MSG_INFO_EVT_FLAG(flag) \ + WMI_GET_BITS(flag, 20, 1) +#define WMI_SET_BLACKLIST_MSG_INFO_EVT_FLAG(flag, val) \ + WMI_SET_BITS(flag, 20, 1, val) + +#define WMI_GET_BLACKLIST_CHANNELS_TOTAL_NUM_CHAN(flag) \ + WMI_GET_BITS(flag, 0, 16) +#define WMI_SET_BLACKLIST_CHANNELS_TOTAL_NUM_CHAN(flag, val) \ + WMI_SET_BITS(flag, 0, 16, val) + +#define WMI_GET_BLACKLIST_CHANNELS_CURRENT_NUM_CHAN(flag) \ + WMI_GET_BITS(flag, 16, 16) +#define WMI_SET_BLACKLIST_CHANNELS_CURRENT_NUM_CHAN(flag, val) \ + WMI_SET_BITS(flag, 16, 16, val) + +typedef struct { + /** TLV tag and len; + * tag equals WMITLV_TAG_STRUC_wmi_hw_blacklist_chan_fixed_param*/ + A_UINT32 tlv_header; + A_UINT32 phy_id; + union { + A_UINT32 blacklist_msg_info; + /* + * bit 7 - 0 -> Current WMI event sequence number + * bit 15 - 8 -> Total number of WMI events [For debugging only] + * bit 19 - 16 -> ENUM "WMI_HW_BLACKLIST_CHAN_RESP_CMD_CODE" + * bit 20 -> ENUM "WMI_HW_BLACKLIST_CHAN_DATA_EVENT_FLAG" + * bit 31 - 21 -> Reserved + */ + struct { + A_UINT32 + blacklist_wmi_seq_num: 8, + blacklist_wmi_total_num: 8, + blacklist_resp_code: 4, + blacklist_event_flag:1, + reserved: 11; + }; + }; + /* + * bit 15 - 0 -> Total number of HW blacklist channels + * bit 31 - 16 -> number of HW blacklist channels in current WMI + */ + union { + A_UINT32 num_hw_blacklist_channels; + struct { + A_UINT32 + total_num_chan: 16, + current_num_chan: 16; + }; + }; +} wmi_hw_blacklist_chan_fixed_param; + +#define WMI_GET_FREQ_INFO_PRI20_BITMAP(flag) \ + WMI_GET_BITS(flag, 0, 16) +#define WMI_SET_FREQ_INFO_PRI20_BITMAP(flag, val) \ + WMI_SET_BITS(flag, 0, 16, val) + +#define WMI_GET_FREQ_INFO_CHAN_CENTER_FREQ(flag) \ + WMI_GET_BITS(flag, 16, 16) +#define WMI_SET_FREQ_INFO_CHAN_CENTER_FREQ(flag, val) \ + WMI_SET_BITS(flag, 16, 16, val) + +#define WMI_GET_CHAN_LIST_META_DATA_POWER_MODE(flag) \ + WMI_GET_BITS(flag, 0, 4) +#define WMI_SET_CHAN_LIST_META_DATA_POWER_MODE(flag, val) \ + WMI_SET_BITS(flag, 0, 4, val) + +#define WMI_GET_CHAN_LIST_META_DATA_MAX_BW(flag) \ + WMI_GET_BITS(flag, 4, 8) +#define WMI_SET_CHAN_LIST_META_DATA_MAX_BW(flag, val) \ + WMI_SET_BITS(flag, 4, 8, val) + +#define WMI_GET_PUNCTURE_PATTERN_BITMAP(flag) \ + WMI_GET_BITS(flag, 0, 24) +#define WMI_SET_PUNCTURE_PATTERN_BITMAP(flag, val) \ + WMI_SET_BITS(flag, 0, 24, val) + +typedef struct { + /** TLV tag and len; + * tag equals WMITLV_TAG_STRUC_wmi_hw_blacklist_chan_data */ + A_UINT32 tlv_header; + /* + * bit 15 - 0 -> bitmap representing a set of 20MHz primary channels + * bit 31 - 16 -> channel center frequency in MHz for the given Max BW + * in "chan_list_meta_data" + */ + union { + A_UINT32 freq_info; + struct { + A_UINT32 + pri20_bitmap: 16, + chan_center_freq: 16; + }; + }; + /* + * bit 3 - 0 -> Power mode "WMI_6GHZ_REG_PWRMODE_TYPE" + * bit 11 - 4 -> Max BW with enum "wmi_channel_width" + * bit 31 - 12 -> reserved + */ + union { + A_UINT32 chan_list_meta_data; + struct { + A_UINT32 + power_mode: 4, + max_bw: 8, + reserved1: 20; + }; + }; + /* + * bit represents blacklisted puncture pattern based on enums + * WMI_11BE_PUNCTURE_PATTERNS_320MHZ, WMI_11BE_PUNCTURE_PATTERNS_160MHZ, + * WMI_11BE_PUNCTURE_PATTERNS_80MHZ + * bit 23 - 0 -> bitmap representing a set of puncture patterns of the + * given bandwidth. The bandwidth is represented by + * bits 8-15 of A_UINT32 chan_list_meta_data + * bit 31 - 24 -> reserved for future puncture patterns + */ + union { + A_UINT32 puncture_pattern_bitmap_info; + struct { + A_UINT32 + puncture_pattern_bitmap: 24, + reserved2: 8; + }; + }; +} wmi_hw_blacklist_chan_data; + typedef struct { A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_11d_scan_start_cmd_fixed_param */ A_UINT32 vdev_id; @@ -39965,7 +41015,10 @@ typedef struct { #define WLM_FLAGS_ROAM_SUPPRESS 1 #define WLM_FLAGS_ALLOW_FINAL_BMISS_ROAM 2 -/* bit 8: reserved for roaming */ +/* bit 8: Final bmiss roam will be triggered when all active links + * are final bmiss reported. + */ +#define WLM_FLAGS_ROAM_WHEN_ALL_LINKS_FBMISS 1 /* bit 9-11 of flags is used for powersave operation */ /* bit 9: WLM_FLAGS_PS_DISABLE_BMPS, disable BMPS if bit is set */ @@ -40009,6 +41062,8 @@ typedef struct { #define WLM_FLAGS_TSF_LATENCY_COMPENSATE_ENABLED_SET(flag) WMI_SET_BITS(flag, 4, 1, val) #define WLM_FLAGS_ROAM_GET_POLICY(flag) WMI_GET_BITS(flag, 6, 2) #define WLM_FLAGS_ROAM_SET_POLICY(flag, val) WMI_SET_BITS(flag, 6, 2, val) +#define WLM_FLAGS_ROAM_GET_WHEN_ALL_LINKS_FBMISS(flag) WMI_GET_BITS(flag, 8, 1) +#define WLM_FLAGS_ROAM_SET_WHEN_ALL_LINKS_FBMISS(flag, val) WMI_SET_BITS(flag, 8, 1, val) #define WLM_FLAGS_PS_IS_BMPS_DISABLED(flag) WMI_GET_BITS(flag, 9, 1) #define WLM_FLAGS_PS_IS_CSS_CLPS_DISABLED(flag) WMI_GET_BITS(flag, 10, 1) #define WLM_FLAGS_PS_SET_CSS_CLPS_DISABLE(flag, val) WMI_SET_BITS(flag, 10, 1, val) @@ -40518,6 +41573,9 @@ typedef enum _WMI_DEL_TWT_STATUS_T { WMI_DEL_TWT_STATUS_CHANGE_CONGESTION_TIMEOUT, /* Congestion timeout changed */ WMI_DEL_TWT_STATUS_P2P_GO_NOA, /* P2P GO NOA */ WMI_DEL_TWT_STATUS_UNSUPPORTED_MLMR_MODE, /* Teardown due to MLMR */ + WMI_DEL_TWT_STATUS_MLO_LINK_INACTIVE, /* Teardown due to link going to inactive */ + WMI_DEL_TWT_STATUS_2G_TWT_NOT_ENABLED, /* Teardown due to 2.4 GHz TWT not enabled */ + WMI_DEL_TWT_STATUS_SCAN_STARTED, /* Teardown due to scan started */ } WMI_DEL_TWT_STATUS_T; typedef struct { @@ -41225,6 +42283,11 @@ typedef struct { A_UINT32 vdev_id; /* 1-Enable, 0-Disable */ A_UINT32 enable; + /* self_roaming_re_enable_time: + * Allow next deauth self-roaming only when time gap is more than + * self_roaming_re_enable_time w.r.t previous deauth self-roaming. + */ + A_UINT32 self_roaming_re_enable_time; /* units = seconds */ } wmi_roam_deauth_config_cmd_fixed_param; /** IDLE roam trigger parameters */ @@ -41753,6 +42816,8 @@ typedef enum { WMI_ROAM_FAIL_REASON_CURR_AP_STILL_OK, /* Roam scan not happen due to current network condition is fine */ WMI_ROAM_FAIL_REASON_SCAN_CANCEL, /* Roam scan canceled */ WMI_ROAM_FAIL_REASON_MLD_EXTRA_SCAN_REQUIRED, /* Roaming is not triggered for current roam scan as extra scan is required to scan all MLD links */ + WMI_ROAM_FAIL_REASON_TTLM_REQUIRED, /* Roaming is not triggered as TTLM is required */ + WMI_ROAM_FAIL_REASON_LINKRECONFIG_REQUIRED, /* Roaming is not triggered as linkreconfig is required */ WMI_ROAM_FAIL_REASON_UNKNOWN = 255, } WMI_ROAM_FAIL_REASON_ID; @@ -41937,7 +43002,7 @@ typedef struct { * rssi_dbm_abs * Last known RSSI of the current BSSID at the moment when the frame * was sent and received. - * This RSSI value is valid for deauth / disassoc frame only. + * Host should ignore this field if its value is 0. * The rssi_dbm_abs value is the absolute value of the RSSI in dBm units. * For example, if the RSSI is -40 dBm, rssi_dbm_abs will be 40. */ @@ -42500,6 +43565,18 @@ typedef enum { */ WMI_ROAM_PARAM_ROAM_LATENCY_OPTIMIZATION_BITMAP = 11, + /* + * Roam param to add RSSI penalty for non-6GHz Candidate AP + * during Roam Scan in case current connected AP is 6GHz and + * cand AP is non-6GHz. + * This RSSI penalty value (in dB units) for non-6GHz candidate AP + * will be configured via ini roam_rssi_delta_from_6ghz_to_non_6ghz. + * This configured RSSI penalty value will only be applicable for non-6GHz + * Candidate AP when the STA is connected to 6GHz Band AP and will + * not impact if STA is connected to non-6GHz Band AP + */ + WMI_ROAM_PARAM_ROAM_RSSI_PENALTY_FOR_NON_6GHZ_CAND_AP = 12, + /*=== END ROAM_PARAM_PROTOTYPE SECTION ===*/ } WMI_ROAM_PARAM; @@ -44757,13 +45834,42 @@ typedef struct { /****** End of 11BE EHT MAC Capabilities Information field ******/ -/****** 11BE EHT OPS Information field ******/ - -/* Bit 0 is for MCS15. If bit0 is 1 then we enable mcs15 */ +/****** 11BE EHT OPS Information field - DEPRECATED ******/ +/* DEPRECATED, replaced by + * "Bit 6 is for MCS15. If bit6 is 1 then we disable mcs15" + * OLD: Bit 0 is for MCS15. If bit0 is 1 then we enable mcs15 + */ #define WMI_EHT_OPS_SUPMCS15_GET(eht_ops) WMI_GET_BITS(eht_ops, 0, 1) #define WMI_EHT_OPS_SUPMCS15_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 0, 1, value) +/****** End of 11BE EHT OPS Information field - DEPRECATED ******/ +/****** 11BE EHT Operation Parameters field ******/ +/* Bit 0 EHT Operation Information Present */ +#define WMI_EHT_OPS_INFORMATION_PRESENT_GET(eht_ops) WMI_GET_BITS(eht_ops, 0, 1) +#define WMI_EHT_OPS_INFORMATION_PRESENT_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 0, 1, value) + +/* Bit 1 Disabled Subchannel Bitmap Present */ +#define WMI_EHT_OPS_DISABLED_SUBCHANNEL_BITMAP_GET(eht_ops) WMI_GET_BITS(eht_ops, 1, 1) +#define WMI_EHT_OPS_DISABLED_SUBCHANNEL_BITMAP_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 1, 1, value) + +/* Bit 2 EHT default PE duration */ +#define WMI_EHT_OPS_PE_DURATION_GET(eht_ops) WMI_GET_BITS(eht_ops, 2, 1) +#define WMI_EHT_OPS_PE_DURATION_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 2, 1, value) + +/* Bit 3 Group Addressed BU indication limit*/ +#define WMI_EHT_OPS_GROUP_ADDRESSED_BU_INDICATION_LIMIT_GET(eht_ops) WMI_GET_BITS(eht_ops, 3, 1) +#define WMI_EHT_OPS_GROUP_ADDRESSED_BU_INDICATION_LIMIT_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 3, 1, value) + +/* Bit 4~5 Group Addressed BU indication Exponent */ +#define WMI_EHT_OPS_GROUP_ADDRESSED_BU_INDICATION_EXPONENT_GET(eht_ops) WMI_GET_BITS(eht_ops, 4, 2) +#define WMI_EHT_OPS_GROUP_ADDRESSED_BU_INDICATION_EXPONENT_SET(eht_ops) WMI_SET_BITS(eht_ops, 4, 2, value) + +/* Bit 6 is for MCS15. If bit6 is 1 then we disable mcs15 */ +#define WMI_EHT_OPS_MCS15_DISABLE_GET(eht_ops) WMI_GET_BITS(eht_ops, 6, 1) +#define WMI_EHT_OPS_MCS15_DISABLE_SET(eht_ops, value) WMI_SET_BITS(eht_ops, 6, 1, value) + +/* Bit 7: reserved */ +/****** End of 11BE EHT Operation Parameters field ******/ -/****** End of 11BE EHT OPS Information field ******/ typedef struct { /** TLV tag and len; tag equals @@ -46703,6 +47809,7 @@ typedef enum { WMI_MLO_LINK_FORCE_REASON_TDLS = 4, /* Set force specific links because of 11BE MLO TDLS setup/teardown */ WMI_MLO_LINK_FORCE_REASON_REVERT_FAILURE = 5, /* Set force specific links for revert previous failed due to host reject */ WMI_MLO_LINK_FORCE_REASON_LINK_DELETE = 6, /* Set force specific links because link is deleted from associated link set */ + WMI_MLO_LINK_FORCE_REASON_SINGLE_LINK_EMLSR_OP = 7, /* Set force specific links because single link eMLSR operation */ } WMI_MLO_LINK_FORCE_REASON; #define WMI_MLO_CONTROL_FLAGS_GET_OVERWRITE_FORCE_ACTIVE(mlo_flags) \ @@ -47033,11 +48140,30 @@ typedef struct { /* MLD address of AP */ wmi_mac_addr mld_addr; /* any non-zero values of status indicate link reconfig failure. */ - A_UINT32 status; - /* valid only when status is non-zero. fw will do reassociation if link reconfig failure */ - A_UINT32 reassoc_if_failure; + A_UINT32 status; + /* reassoc_if_failure: + * Valid only when status is non-zero. + * FW will do reassociation if link reconfig failure. + */ + A_UINT32 reassoc_if_failure; } wmi_mlo_link_reconfig_complete_fixed_param; +typedef struct { + /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_mlo_link_ttlm_complete_fixed_param */ + A_UINT32 tlv_header; + /* unique id identifying the VDEV, generated by the caller */ + A_UINT32 vdev_id; + /* MLD address of AP */ + wmi_mac_addr mld_addr; + /* any non-zero values of status indicate link TTLM failure. */ + A_UINT32 status; + /* reassoc_if_failure: + * Valid only when status is non-zero. + * FW will do reassociation if link reconfig failure. + */ + A_UINT32 reassoc_if_failure; +} wmi_mlo_link_ttlm_complete_fixed_param; + #define WMI_TID_TO_LINK_MAP_TID_NUM_GET(_var) WMI_GET_BITS(_var, 0, 5) #define WMI_TID_TO_LINK_MAP_TID_NUM_SET(_var, _val) WMI_SET_BITS(_var, 0, 5, _val) @@ -47905,9 +49031,11 @@ typedef struct { } wmi_sawf_svc_class_disable_cmd_fixed_param; /* Used to store Hop count info for SDWF-Ezmesh scenario based on topology changes */ +#define WMI_SAWF_EZMESH_HOP_COUNT_SVC_ID_GET(svc_class_params) WMI_GET_BITS(svc_class_params, 0, 8) +#define WMI_SAWF_EZMESH_HOP_COUNT_SVC_ID_SET(svc_class_params, value) WMI_SET_BITS(svc_class_params, 0, 8, value) typedef struct { A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_sawf_ezmesh_hop_count_cmd_fixed_param */ - A_UINT32 peer_id; + A_UINT32 peer_id; /* deprecated field */ A_UINT32 hop_count; /* delay_bound: * Placeholder for future functionality where delay bound will be directly @@ -47915,6 +49043,15 @@ typedef struct { * (units = ms) */ A_UINT32 delay_bound; + wmi_mac_addr mac_address; /* Mac Address of next BSTA */ + A_UINT32 vdev_id; + union { + struct { + A_UINT32 svc_id:8, + reserved:24; + }; + A_UINT32 svc_class_params; + }; } wmi_sawf_ezmesh_hop_count_cmd_fixed_param; typedef struct { @@ -49815,7 +50952,10 @@ typedef struct { } wmi_mlo_link_del_param; typedef enum { - WMI_EVENT_POWER_BOOST_START_TRAINING = 0, + WMI_EVENT_POWER_BOOST_START_INFERENCING = 0, + /* alias */ + WMI_EVENT_POWER_BOOST_START_TRAINING = + WMI_EVENT_POWER_BOOST_START_INFERENCING, WMI_EVENT_POWER_BOOST_ABORT, WMI_EVENT_POWER_BOOST_COMPLETE, @@ -49828,6 +50968,8 @@ typedef enum { WMI_PDEV_POWER_BOOST_TS_MAX } wmi_pdev_power_boost_training_stage; +typedef wmi_pdev_power_boost_training_stage + wmi_pdev_power_boost_inferencing_stage; /* alias */ typedef struct { /* WMITLV_TAG_STRUC_wmi_pdev_power_boost_event_fixed_param */ @@ -49836,11 +50978,14 @@ typedef struct { A_UINT32 pdev_id; /* enum wmi_pdev_power_boost_event_type to update the power boost status */ A_UINT32 status; - /* training_stage: - * The training stage for which the I/Q samples are updated in DDR. - * This field holds a wmi_pdev_power_boost_training_stage value. + /* inferencing_stage: + * The inferencing stage for which the I/Q samples are updated in DDR. + * This field holds a wmi_pdev_power_boost_inferencing_stage value. */ - A_UINT32 training_stage; + union { + A_UINT32 training_stage; /* deprecated name */ + A_UINT32 inferencing_stage; /* preferred name */ + }; /* MCS value for which the current DPD training has been done */ A_UINT32 mcs; /* bandwidth: @@ -49865,6 +51010,16 @@ typedef struct { * in units of KB */ A_UINT32 size_kb; + /* tx_pwr: + * TX POWER of ANN PBT INFERENCING PKT (dBm units) + */ + A_INT32 tx_pwr; + /* tx_chain_idx: + * CHAIN INDEX where WMI is sent to HOST to start inferencing + */ + A_UINT32 tx_chain_idx; + /* req_id: to distinguish pdev_power_boost event instances */ + A_UINT32 req_id; } wmi_pdev_power_boost_event_fixed_param; typedef enum { @@ -49882,8 +51037,14 @@ typedef struct { A_UINT32 pdev_id; /* enum wmi_pdev_power_boost_cmd_type to update the power boost status */ A_UINT32 status; - /* wmi_pdev_power_boost_training_stage value to indicate training stage */ - A_UINT32 training_stage; + /* + * wmi_pdev_power_boost_inferencing_stage value to indicate + * inferencing stage + */ + union { + A_UINT32 training_stage; /* deprecated name */ + A_UINT32 inferencing_stage; /* preferred name */ + }; /* MCS value for which the Power Boost training has been done */ A_UINT32 mcs; /* Bandwidth in Mhz for which the Power Boost training has been done */ @@ -49911,6 +51072,8 @@ typedef struct { * training, in units of 1/4 (0.25dBm) steps. */ A_INT32 tx_mask_margin; + /* req_id: to distinguish pdev_power_boost cmd instances */ + A_UINT32 req_id; } wmi_pdev_power_boost_cmd_fixed_param; typedef struct { @@ -50059,8 +51222,9 @@ typedef struct { }; /* * The below TLVs follow this TLV in the WMI_VDEV_VBSS_CONFIG_CMDID msg: - * - wmi_vdev_vbss_peer_sn_info[]; * - wmi_vdev_vbss_peer_pn_info[]; + * - wmi_vdev_vbss_peer_sn_info[]; + * - wmi_vdev_vbss_peer_dyn_info; */ } wmi_vdev_vbss_config_cmd_fixed_param; @@ -50093,15 +51257,38 @@ typedef struct { ssn: 16; }; }; - /* The below TLVs follow this TLV in the WMI_VDEV_VBSS_CONFIG_EVENTID msg: - * - A_UINT32 scan_freq_list[]; - * - wmi_vdev_vbss_config_event_fixed_param[]; - */ } wmi_vdev_vbss_peer_sn_info; +typedef struct { + A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vdev_vbss_peer_dyn_info */ + union { + A_UINT32 peer_dyn_info1_word32; + struct { + /** 15:0 omi + * 31:16 eht_omi + */ + A_UINT32 + omi: 16, + eht_omi: 16; + }; + }; + union { + A_UINT32 peer_dyn_info2_word32; + struct { + /** 1:0 pm + * 31:2 reserved + */ + A_UINT32 + pm : 1, + rsvd : 31; + }; + }; +} wmi_vdev_vbss_peer_dyn_info; + typedef enum { - WMI_VBSS_GET_PEER_CONTEXT = 0x1, - WMI_VBSS_SET_PEER_CONTEXT = 0x2, + WMI_VBSS_GET_PEER_CONTEXT = 1, + WMI_VBSS_SET_PEER_CONTEXT = 2, + WMI_VBSS_RX_SUSPEND_PEER_CONTEXT = 3, } wmi_vbss_action; #define WMI_VDEV_VBSS_GET_ACTION(action) WMI_GET_BITS(action, 0, 4) @@ -50116,6 +51303,15 @@ typedef enum { #define WMI_VDEV_VBSS_SN_INFO_GET_SSN(tid_num_ssn) WMI_GET_BITS(tid_num_ssn, 16, 16) #define WMI_VDEV_VBSS_SN_INFO_SET_SSN(action, value) WMI_SET_BITS(tid_num_ssn, 16, 16, value) +#define WMI_VDEV_VBSS_DYN_INFO_GET_OMI(peer_dyn_info1) WMI_GET_BITS(omi, 0, 16) +#define WMI_VDEV_VBSS_DYN_INFO_SET_OMI(peer_dyn_info1, value) WMI_SET_BITS(omi, 0, 16, value) + +#define WMI_VDEV_VBSS_DYN_INFO_GET_EHT_OMI(peer_dyn_info1) WMI_GET_BITS(eht_omi, 16, 16) +#define WMI_VDEV_VBSS_DYN_INFO_SET_EHT_OMI(peer_dyn_info1, value) WMI_SET_BITS(eht_omi, 16, 16, value) + +#define WMI_VDEV_VBSS_DYN_INFO_GET_PM(peer_dyn_info2) WMI_GET_BITS(pm, 0, 1) +#define WMI_VDEV_VBSS_DYN_INFO_SET_PM(peer_dyn_info2, value) WMI_SET_BITS(pm, 0, 1, value) + typedef struct { A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vdev_vbss_config_event_fixed_param */ @@ -50130,6 +51326,93 @@ typedef struct { } wmi_vdev_vbss_config_event_fixed_param; +typedef enum { + /* Channel bandwidth based semi static PCIe config */ + WMI_PCIE_CONFIG_TYPE_CHANNEL_BANDWIDTH, + /* Force a specific PCIe config */ + WMI_PCIE_CONFIG_TYPE_FORCED_STATIC, +} wmi_pcie_config_type_e; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_config_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable PCIe config */ + A_UINT32 enable; + /* PCIe config type (holds a wmi_pcie_config_type_e value) */ + A_UINT32 config_type; + /** pcie_gen: pcie generation to be used + * pcie_lane:pcie lane config (lane number) to be used + */ + A_UINT32 pcie_gen; + A_UINT32 pcie_lane; +} wmi_energy_mgmt_pcie_config_cmd_fixed_param; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_pcie_lpm_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable PCIe LPM */ + A_UINT32 enable; +} wmi_energy_mgmt_pcie_lpm_cmd_fixed_param; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_dcvs_config_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable DCVS config */ + A_UINT32 enable; +} wmi_energy_mgmt_dcvs_config_cmd_fixed_param; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_dps_config_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable DPS config */ + A_UINT32 enable; + /** PDEV identifier */ + A_UINT32 pdev_id; + /** Channel to be used in DPS Low cap mode, freq must match the primary home chan freq */ + wmi_channel low_cap_channel; + /** Tx chainmask to be used in DPS Low cap mode */ + A_UINT32 low_cap_tx_chainmask; + /** Rx chainmask to be used in DPS Low cap mode */ + A_UINT32 low_cap_rx_chainmask; + /** Mbps throughput after which DPS will be exited and notified to Host */ + A_UINT32 exit_thpt_thrsld_mbps; + /** number of wakeups per second after which DPS will be exited and notified to Host */ + A_UINT32 exit_wakeup_thrsld_per_second; +} wmi_energy_mgmt_edps_config_cmd_fixed_param; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_puo_config_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable DPS config */ + A_UINT32 enable; + /** PDEV identifier */ + A_UINT32 pdev_id; + /** Channel to be used in PUO Low cap mode, freq must match the primary home chan freq */ + wmi_channel low_cap_channel; + /** Tx chainmask to be used in Low cap mode */ + A_UINT32 low_cap_tx_chainmask; + /** Rx chainmask to be used in Low cap mode */ + A_UINT32 low_cap_rx_chainmask; + /** minimum duration required between SP end and SP start to trigger entering to doze state */ + A_UINT32 min_doze_thrsld_in_us; +} wmi_energy_mgmt_puo_config_cmd_fixed_param; + +typedef struct { + /** TLV tag and len; tag equals + * WMITLV_TAG_STRUC_wmi_energy_mgmt_eco_mode_config_cmd_fixed_param */ + A_UINT32 tlv_header; + /** enable or disable DPS config */ + A_UINT32 enable; + /** PDEV identifier */ + A_UINT32 pdev_id; +} wmi_energy_mgmt_eco_mode_config_cmd_fixed_param; + + /* ADD NEW DEFS HERE */ diff --git a/drivers/staging/fw-api/fw/wmi_version.h b/drivers/staging/fw-api/fw/wmi_version.h index 145e8fcc2aa4..2c695cde4cda 100644 --- a/drivers/staging/fw-api/fw/wmi_version.h +++ b/drivers/staging/fw-api/fw/wmi_version.h @@ -37,7 +37,7 @@ #define __WMI_VER_MINOR_ 0 /** WMI revision number has to be incremented when there is a * change that may or may not break compatibility. */ -#define __WMI_REVISION_ 1575 +#define __WMI_REVISION_ 1637 /** The Version Namespace should not be normally changed. Only * host and firmware of the same WMI namespace will work diff --git a/drivers/staging/qca-wifi-host-cmn/qdf/linux/src/qdf_trace.c b/drivers/staging/qca-wifi-host-cmn/qdf/linux/src/qdf_trace.c index 0b3e2dbd150d..30815cb19d89 100644 --- a/drivers/staging/qca-wifi-host-cmn/qdf/linux/src/qdf_trace.c +++ b/drivers/staging/qca-wifi-host-cmn/qdf/linux/src/qdf_trace.c @@ -411,10 +411,19 @@ void qdf_mtrace_log(QDF_MODULE_ID src_module, QDF_MODULE_ID dst_module, uint16_t message_id, uint8_t vdev_id) { uint32_t trace_log, payload; - static uint16_t counter; + static __qdf_atomic_t counter; + static bool initialized = false; + + // Initialize counter only once + if (!initialized) { + qdf_atomic_init(&counter); + initialized = true; + } trace_log = (src_module << 23) | (dst_module << 15) | message_id; - payload = (vdev_id << 16) | counter++; + + qdf_atomic_add(1, &counter); + payload = ((uint32_t)vdev_id << 16) | (qdf_atomic_read(&counter) & 0xFFFF); QDF_TRACE(src_module, QDF_TRACE_LEVEL_TRACE, "%x %x", trace_log, payload); diff --git a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_build_chan_list.c b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_build_chan_list.c index 6059b406d57b..40959dbe6df2 100644 --- a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_build_chan_list.c +++ b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_build_chan_list.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -2089,6 +2089,11 @@ QDF_STATUS reg_process_master_chan_list_ext( reg_store_regulatory_ext_info_to_socpriv(soc_reg, regulat_info, phy_id); + if (this_mchan_params->client_type >= REG_MAX_CLIENT_TYPE) { + reg_err("6 GHz reg client type invalid"); + return QDF_STATUS_E_FAILURE; + } + status = reg_fill_master_channels(regulat_info, &this_mchan_params->reg_rules, this_mchan_params->client_type, diff --git a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.c b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.c index 7732e35b5ee4..328a4898dcd1 100644 --- a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.c +++ b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022,2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -5113,7 +5113,7 @@ QDF_STATUS reg_get_6g_chan_ap_power(struct wlan_objmgr_pdev *pdev, QDF_STATUS reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power) { @@ -5136,8 +5136,7 @@ QDF_STATUS reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, reg_find_txpower_from_6g_list(chan_freq, master_chan_list, tx_power); - *is_psd = reg_is_6g_psd_power(pdev); - if (*is_psd) + if (is_psd) status = reg_get_6g_chan_psd_eirp_power(chan_freq, master_chan_list, eirp_psd_power); diff --git a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.h b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.h index c0d478ea1887..3a94a215298a 100644 --- a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.h +++ b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/core/src/reg_services_common.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * * Permission to use, copy, modify, and/or distribute this software for @@ -1484,7 +1484,7 @@ QDF_STATUS reg_get_6g_chan_ap_power(struct wlan_objmgr_pdev *pdev, * * This function is meant to be called to find the channel frequency power * information for a client when the device is operating as a client. It will - * fill in the parameter is_psd, tx_power, and eirp_psd_power. eirp_psd_power + * fill in the parameters tx_power and eirp_psd_power. eirp_psd_power * will only be filled if the channel is PSD. * * Return: QDF_STATUS @@ -1492,7 +1492,7 @@ QDF_STATUS reg_get_6g_chan_ap_power(struct wlan_objmgr_pdev *pdev, QDF_STATUS reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power); @@ -1582,11 +1582,10 @@ static inline QDF_STATUS reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power) { - *is_psd = false; *tx_power = 0; *eirp_psd_power = 0; return QDF_STATUS_E_NOSUPPORT; diff --git a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h index d8c7ad95a133..c380a077abe5 100644 --- a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h +++ b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/inc/wlan_reg_services_api.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1869,8 +1869,8 @@ QDF_STATUS wlan_reg_get_6g_chan_ap_power(struct wlan_objmgr_pdev *pdev, * * This function is meant to be called to find the channel frequency power * information for a client when the device is operating as a client. It will - * fill in the parameter is_psd, tx_power, and eirp_psd_power. eirp_psd_power - * will only be filled if the channel is PSD. + * fill in the parameters tx_power and eirp_psd_power. eirp_psd_power will + * only be filled if the channel is PSD. * * Return: QDF_STATUS */ @@ -1878,7 +1878,7 @@ QDF_STATUS wlan_reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, uint16_t *tx_power, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power); /** @@ -1985,10 +1985,9 @@ static inline QDF_STATUS wlan_reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, uint16_t *tx_power, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power) { - *is_psd = false; *tx_power = 0; *eirp_psd_power = 0; return QDF_STATUS_E_NOSUPPORT; diff --git a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/src/wlan_reg_services_api.c b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/src/wlan_reg_services_api.c index 2b720dee6ca6..7d458f0421fc 100644 --- a/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/src/wlan_reg_services_api.c +++ b/drivers/staging/qca-wifi-host-cmn/umac/regulatory/dispatcher/src/wlan_reg_services_api.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * * Permission to use, copy, modify, and/or distribute this software for @@ -1443,7 +1443,7 @@ QDF_STATUS wlan_reg_get_client_power_for_connecting_ap(struct wlan_objmgr_pdev *pdev, enum reg_6g_ap_type ap_type, qdf_freq_t chan_freq, - bool *is_psd, uint16_t *tx_power, + bool is_psd, uint16_t *tx_power, uint16_t *eirp_psd_power) { return reg_get_client_power_for_connecting_ap(pdev, ap_type, chan_freq, diff --git a/drivers/staging/qca-wifi-host-cmn/wmi/src/wmi_unified_tlv.c b/drivers/staging/qca-wifi-host-cmn/wmi/src/wmi_unified_tlv.c index cd998b35ca7e..b9359e099ce6 100644 --- a/drivers/staging/qca-wifi-host-cmn/wmi/src/wmi_unified_tlv.c +++ b/drivers/staging/qca-wifi-host-cmn/wmi/src/wmi_unified_tlv.c @@ -14208,9 +14208,14 @@ extract_roam_scan_ap_stats_tlv(wmi_unified_t wmi_handle, void *evt_buf, return QDF_STATUS_E_FAILURE; } - if (ap_idx >= param_buf->num_roam_ap_info) { - wmi_err("Invalid roam scan AP tlv ap_idx:%d total_ap:%d", - ap_idx, param_buf->num_roam_ap_info); + /* + * Check to validate that the requested number of APs do not exceed the + * remaining APs in param_buf after ap_idx to prevent out of bounds + * access. + */ + if ((ap_idx + num_cand) > param_buf->num_roam_ap_info) { + wmi_err("Invalid roam scan AP tlv ap_idx:%d, num_cand:%d, total_ap:%d", + ap_idx, num_cand, param_buf->num_roam_ap_info); return QDF_STATUS_E_FAILURE; } @@ -14724,6 +14729,12 @@ static QDF_STATUS extract_pdev_csa_switch_count_status_tlv( wmi_handle, csa_status->pdev_id); param->current_switch_count = csa_status->current_switch_count; + + if (param_buf->num_vdev_ids != csa_status->num_vdevs) { + wmi_err("Invalid number of vdevs: received = %d, expected = %d", + csa_status->num_vdevs, param_buf->num_vdev_ids); + return QDF_STATUS_E_INVAL; + } param->num_vdevs = csa_status->num_vdevs; param->vdev_ids = param_buf->vdev_ids; diff --git a/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c index 66f4ef140432..131518cc5cd9 100644 --- a/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c +++ b/drivers/staging/qcacld-3.0/components/mlme/core/src/wlan_mlme_main.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1846,6 +1847,8 @@ static void mlme_init_lfr_cfg(struct wlan_objmgr_psoc *psoc, cfg_get(psoc, CFG_LFR3_ROAM_PREAUTH_RETRY_COUNT); lfr->roam_rssi_diff = cfg_get(psoc, CFG_LFR_ROAM_RSSI_DIFF); lfr->roam_rssi_diff_6ghz = cfg_get(psoc, CFG_LFR_ROAM_RSSI_DIFF_6GHZ); + lfr->roam_rssi_delta_6ghz_to_non_6ghz = + cfg_get(psoc, CFG_LFR_ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ); lfr->bg_rssi_threshold = cfg_get(psoc, CFG_LFR_ROAM_BG_RSSI_TH); lfr->roam_scan_offload_enabled = cfg_get(psoc, CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED); diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h index 6ad5d7011e58..135eb7597a3c 100644 --- a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/cfg_mlme_lfr.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1280,6 +1281,38 @@ CFG_VALUE_OR_DEFAULT, \ "Enable 6 GHz roam based on rssi") +/* + * + * roam_rssi_delta_from_6ghz_to_non_6ghz - Enable roam to Non 6 GHz AP based + * on rssi + * @Min: 0 + * @Max: 100 + * @Default: 0 + * + * This INI is used to decide whether to roam to Non 6 GHz AP or not based on + * RSSI. AP1 is the currently associated AP(6 GHz) and AP2(2.4 GHz / 5 GHz) is + * chosen for roaming. The Roaming will happen only if AP2 has better Signal + * Quality and it has a RSSI better than AP1. + * roam_rssi_delta_from_6ghz_to_non_6ghz is the number of dB units AP2 is + * better than AP1. + * + * + * Related: None + * + * Supported Feature: Roaming + * + * Usage: External + * + * + */ +#define CFG_LFR_ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ CFG_INI_UINT( \ + "roam_rssi_delta_from_6ghz_to_non_6ghz", \ + 0, \ + 100, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable 6 GHz to non 6 GHz roam based on rssi") + /* * * bg_rssi_threshold - To set RSSI Threshold for BG scan roaming @@ -2930,6 +2963,7 @@ CFG(CFG_LFR_FAST_TRANSITION_ENABLED) \ CFG(CFG_LFR_ROAM_RSSI_DIFF) \ CFG(CFG_LFR_ROAM_RSSI_DIFF_6GHZ) \ + CFG(CFG_LFR_ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ) \ CFG(CFG_LFR_ROAM_BG_RSSI_TH) \ CFG(CFG_LFR_ENABLE_WES_MODE) \ CFG(CFG_LFR_ROAM_SCAN_OFFLOAD_ENABLED) \ diff --git a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h index ad48cd35c0cd..9ca94e78d468 100644 --- a/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h +++ b/drivers/staging/qcacld-3.0/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1680,8 +1681,10 @@ struct fw_scan_channels { * @roam_preauth_no_ack_timeout: Configure the no ack timeout period * @roam_rssi_diff: Enable roam based on rssi * @roam_rssi_diff_6ghz: RSSI diff value to be used for roaming to 6 GHz AP. - * @roam_scan_offload_enabled: Enable Roam Scan Offload - * @neighbor_scan_timer_period: Neighbor scan timer period + * @roam_rssi_delta_6ghz_to_non_6ghz: RSSI diff value to be used for + * roaming from 6 GHz to Non 6GHz AP. + * @roam_scan_offload_enabled: Enable Roam Scan Offload + * @neighbor_scan_timer_period: Neighbor scan timer period * @neighbor_scan_min_timer_period: Min neighbor scan timer period * @neighbor_lookup_rssi_threshold: Neighbor lookup rssi threshold * @opportunistic_scan_threshold_diff: Set oppurtunistic threshold diff @@ -1804,6 +1807,7 @@ struct wlan_mlme_lfr_cfg { uint32_t roam_preauth_no_ack_timeout; uint8_t roam_rssi_diff; uint8_t roam_rssi_diff_6ghz; + uint8_t roam_rssi_delta_6ghz_to_non_6ghz; uint8_t bg_rssi_threshold; bool roam_scan_offload_enabled; uint32_t neighbor_scan_timer_period; diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c index 1478df85dce7..857e619900a8 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -211,7 +211,7 @@ static QDF_STATUS p2p_check_and_update_channel(struct tx_action_context *tx_ctx) struct p2p_soc_priv_obj *p2p_soc_obj; struct p2p_roc_context *curr_roc_ctx; - if (!tx_ctx || tx_ctx->chan) { + if (!tx_ctx || tx_ctx->chan_freq) { p2p_err("NULL tx ctx or channel valid"); return QDF_STATUS_E_INVAL; } @@ -232,7 +232,7 @@ static QDF_STATUS p2p_check_and_update_channel(struct tx_action_context *tx_ctx) (mode == QDF_P2P_DEVICE_MODE || mode == QDF_P2P_CLIENT_MODE || mode == QDF_P2P_GO_MODE)) - tx_ctx->chan = curr_roc_ctx->chan; + tx_ctx->chan_freq = curr_roc_ctx->chan_freq; wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); @@ -1068,16 +1068,13 @@ static QDF_STATUS p2p_mgmt_tx(struct tx_action_context *tx_ctx, void *mac_addr; uint8_t pdev_id; struct wlan_objmgr_vdev *vdev; - uint16_t chanfreq = 0; psoc = tx_ctx->p2p_soc_obj->soc; mgmt_param.tx_frame = packet; mgmt_param.frm_len = buf_len; mgmt_param.vdev_id = tx_ctx->vdev_id; mgmt_param.pdata = frame; - if (tx_ctx->chan) - chanfreq = (uint16_t)wlan_chan_to_freq(tx_ctx->chan); - mgmt_param.chanfreq = chanfreq; + mgmt_param.chanfreq = tx_ctx->chan_freq; mgmt_param.qdf_ctx = wlan_psoc_get_qdf_dev(psoc); if (!(mgmt_param.qdf_ctx)) { @@ -1161,7 +1158,7 @@ static QDF_STATUS p2p_roc_req_for_tx_action( p2p_soc_obj = tx_ctx->p2p_soc_obj; roc_ctx->p2p_soc_obj = p2p_soc_obj; roc_ctx->vdev_id = tx_ctx->vdev_id; - roc_ctx->chan = tx_ctx->chan; + roc_ctx->chan_freq = tx_ctx->chan_freq; roc_ctx->duration = tx_ctx->duration; roc_ctx->roc_state = ROC_STATE_IDLE; roc_ctx->roc_type = OFF_CHANNEL_TX; @@ -1804,13 +1801,15 @@ void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) while (QDF_IS_STATUS_SUCCESS(status)) { tx_ctx = qdf_container_of(p_node, struct tx_action_context, node); - p2p_debug("p2p soc object:%pK, tx ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", - p2p_soc_obj, tx_ctx, - tx_ctx->vdev_id, tx_ctx->scan_id, - tx_ctx->roc_cookie, tx_ctx->chan, - tx_ctx->buf, tx_ctx->buf_len, - tx_ctx->off_chan, tx_ctx->no_cck, - tx_ctx->no_ack, tx_ctx->duration); + p2p_debug("p2p soc object:%pK, tx ctx:%pK, vdev_id:%d, " + "scan_id:%d, roc_cookie:%llx, freq:%d, buf:%pK, " + "len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan_freq, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, p_node, &p_node); @@ -1822,13 +1821,15 @@ void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) while (QDF_IS_STATUS_SUCCESS(status)) { tx_ctx = qdf_container_of(p_node, struct tx_action_context, node); - p2p_debug("p2p soc object:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", - p2p_soc_obj, tx_ctx, - tx_ctx->vdev_id, tx_ctx->scan_id, - tx_ctx->roc_cookie, tx_ctx->chan, - tx_ctx->buf, tx_ctx->buf_len, - tx_ctx->off_chan, tx_ctx->no_cck, - tx_ctx->no_ack, tx_ctx->duration); + p2p_debug("p2p soc object:%pK, tx_ctx:%pK, vdev_id:%d, " + "scan_id:%d, roc_cookie:%llx, freq:%d, buf:%pK, " + "len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan_freq, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, p_node, &p_node); @@ -2918,17 +2919,17 @@ void p2p_rand_mac_tx(struct tx_action_context *tx_action) return; soc = tx_action->p2p_soc_obj->soc; - if (!tx_action->no_ack && tx_action->chan && + if (!tx_action->no_ack && tx_action->chan_freq && tx_action->buf_len > MIN_MAC_HEADER_LEN && p2p_is_vdev_support_rand_mac_by_id(soc, tx_action->vdev_id) && p2p_is_random_mac(soc, tx_action->vdev_id, &tx_action->buf[SRC_MAC_ADDR_OFFSET])) { status = p2p_request_random_mac( - soc, tx_action->vdev_id, - &tx_action->buf[SRC_MAC_ADDR_OFFSET], - wlan_chan_to_freq(tx_action->chan), - tx_action->id, - tx_action->duration); + soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET], + tx_action->chan_freq, + tx_action->id, + tx_action->duration); if (status == QDF_STATUS_SUCCESS) tx_action->rand_mac_tx = true; else @@ -2998,11 +2999,13 @@ QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) p2p_soc_obj = tx_ctx->p2p_soc_obj; - p2p_debug("soc:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", - p2p_soc_obj->soc, tx_ctx, tx_ctx->vdev_id, - tx_ctx->scan_id, tx_ctx->roc_cookie, tx_ctx->chan, - tx_ctx->buf, tx_ctx->buf_len, tx_ctx->off_chan, - tx_ctx->no_cck, tx_ctx->no_ack, tx_ctx->duration); + p2p_debug("soc:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, " + "roc_cookie:%llx, freq:%d, buf:%pK, len:%d, " + "off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj->soc, tx_ctx, tx_ctx->vdev_id, + tx_ctx->scan_id, tx_ctx->roc_cookie, tx_ctx->chan_freq, + tx_ctx->buf, tx_ctx->buf_len, tx_ctx->off_chan, + tx_ctx->no_cck, tx_ctx->no_ack, tx_ctx->duration); status = p2p_get_frame_info(tx_ctx->buf, tx_ctx->buf_len, &(tx_ctx->frame_info)); @@ -3031,8 +3034,8 @@ QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) tx_ctx->no_ack = 1; } - if (!tx_ctx->off_chan || !tx_ctx->chan) { - if (!tx_ctx->chan) + if (!tx_ctx->off_chan || !tx_ctx->chan_freq) { + if (!tx_ctx->chan_freq) p2p_check_and_update_channel(tx_ctx); status = p2p_execute_tx_action_frame(tx_ctx); if (status != QDF_STATUS_SUCCESS) { @@ -3044,7 +3047,7 @@ QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) /* For off channel tx case */ curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); - if (curr_roc_ctx && (curr_roc_ctx->chan == tx_ctx->chan)) { + if (curr_roc_ctx && (curr_roc_ctx->chan_freq == tx_ctx->chan_freq)) { if ((curr_roc_ctx->roc_state == ROC_STATE_REQUESTED) || (curr_roc_ctx->roc_state == ROC_STATE_STARTED)) { tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; @@ -3075,7 +3078,8 @@ QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) } } - curr_roc_ctx = p2p_find_roc_by_chan(p2p_soc_obj, tx_ctx->chan); + curr_roc_ctx = p2p_find_roc_by_chan_freq(p2p_soc_obj, + tx_ctx->chan_freq); if (curr_roc_ctx && (curr_roc_ctx->roc_state == ROC_STATE_IDLE)) { tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; status = qdf_list_insert_back( diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h index 46e80fca5a33..5b98ccbc7066 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_off_chan_tx.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -157,7 +157,7 @@ struct p2p_frame_info { * @scan_id: Scan id given by scan component for this roc req * @roc_cookie: Cookie for remain on channel request * @id: Identifier of this tx context - * @chan: Chan for which this tx has been requested + * @chan_freq: Chan frequency for which this tx has been requested * @buf: tx buffer * @buf_len: Length of tx buffer * @off_chan: Is this off channel tx @@ -174,7 +174,7 @@ struct tx_action_context { int scan_id; uint64_t roc_cookie; int32_t id; - uint8_t chan; + qdf_freq_t chan_freq; uint8_t *buf; int buf_len; bool off_chan; diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c index 7146367eae62..643d54a1a428 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -114,7 +114,7 @@ static QDF_STATUS p2p_scan_start(struct p2p_roc_context *roc_ctx) req->scan_req.scan_type = SCAN_TYPE_P2P_LISTEN; req->scan_req.scan_req_id = p2p_soc_obj->scan_req_id; req->scan_req.chan_list.num_chan = 1; - req->scan_req.chan_list.chan[0].freq = wlan_chan_to_freq(roc_ctx->chan); + req->scan_req.chan_list.chan[0].freq = roc_ctx->chan_freq; req->scan_req.dwell_time_passive = roc_ctx->duration; req->scan_req.dwell_time_active = 0; req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; @@ -278,7 +278,7 @@ static QDF_STATUS p2p_send_roc_event( p2p_evt.vdev_id = roc_ctx->vdev_id; p2p_evt.roc_event = evt; p2p_evt.cookie = (uint64_t)roc_ctx->id; - p2p_evt.chan = roc_ctx->chan; + p2p_evt.chan_freq = roc_ctx->chan_freq; p2p_evt.duration = roc_ctx->duration; p2p_debug("roc_event: %d, cookie:%llx", p2p_evt.roc_event, @@ -305,9 +305,10 @@ static QDF_STATUS p2p_destroy_roc_ctx(struct p2p_roc_context *roc_ctx, QDF_STATUS status = QDF_STATUS_SUCCESS; struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; - p2p_debug("p2p_soc_obj:%pK, roc_ctx:%pK, up_layer_event:%d, in_roc_queue:%d vdev_id:%d chan:%d duration:%d", - p2p_soc_obj, roc_ctx, up_layer_event, in_roc_queue, - roc_ctx->vdev_id, roc_ctx->chan, roc_ctx->duration); + p2p_debug("p2p_soc_obj:%pK, roc_ctx:%pK, up_layer_event:%d," + " in_roc_queue:%d vdev_id:%d freq:%d duration:%d", + p2p_soc_obj, roc_ctx, up_layer_event, in_roc_queue, + roc_ctx->vdev_id, roc_ctx->chan_freq, roc_ctx->duration); if (up_layer_event) { if (roc_ctx->roc_state < ROC_STATE_ON_CHAN) @@ -391,11 +392,13 @@ static void p2p_roc_timeout(void *pdata) return; } - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", - roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, - roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, - roc_ctx->phy_mode, roc_ctx->duration, - roc_ctx->roc_type, roc_ctx->roc_state); + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d," + " tx ctx:%pK, freq:%d, phy_mode:%d, duration:%d," + " roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan_freq, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); if (roc_ctx->roc_state == ROC_STATE_CANCEL_IN_PROG) { p2p_err("Cancellation already in progress"); @@ -418,11 +421,13 @@ static QDF_STATUS p2p_execute_roc_req(struct p2p_roc_context *roc_ctx) QDF_STATUS status; struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", - p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, - roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, - roc_ctx->phy_mode, roc_ctx->duration, - roc_ctx->roc_type, roc_ctx->roc_state); + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d," + " tx ctx:%pK, freq:%d, phy_mode:%d, duration:%d," + " roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan_freq, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); /* prevent runtime suspend */ qdf_runtime_pm_prevent_suspend(&p2p_soc_obj->roc_runtime_lock); @@ -645,14 +650,14 @@ struct p2p_roc_context *p2p_find_current_roc_ctx( struct p2p_roc_context, node); if (roc_ctx->roc_state != ROC_STATE_IDLE) { p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id" - ":%d, scan_id:%d, tx ctx:%pK, chan:" - "%d, phy_mode:%d, duration:%d, " - "roc_type:%d, roc_state:%d", - roc_ctx->p2p_soc_obj, roc_ctx, - roc_ctx->vdev_id, roc_ctx->scan_id, - roc_ctx->tx_ctx, roc_ctx->chan, - roc_ctx->phy_mode, roc_ctx->duration, - roc_ctx->roc_type, roc_ctx->roc_state); + ":%d, scan_id:%d, tx ctx:%pK, freq:" + "%d, phy_mode:%d, duration:%d, " + "roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan_freq, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); return roc_ctx; } @@ -685,8 +690,8 @@ struct p2p_roc_context *p2p_find_roc_by_tx_ctx( return NULL; } -struct p2p_roc_context *p2p_find_roc_by_chan( - struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan) +struct p2p_roc_context *p2p_find_roc_by_chan_freq( + struct p2p_soc_priv_obj *p2p_soc_obj, qdf_freq_t chan_freq) { struct p2p_roc_context *roc_ctx; qdf_list_node_t *p_node; @@ -697,11 +702,14 @@ struct p2p_roc_context *p2p_find_roc_by_chan( roc_ctx = qdf_container_of(p_node, struct p2p_roc_context, node); - if (roc_ctx->chan == chan) { - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + if (roc_ctx->chan_freq == chan_freq) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d," + " scan_id:%d, tx ctx:%pK, freq:%d," + " phy_mode:%d, duration:%d," + " roc_type:%d, roc_state:%d", roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, roc_ctx->scan_id, - roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->tx_ctx, roc_ctx->chan_freq, roc_ctx->phy_mode, roc_ctx->duration, roc_ctx->roc_type, roc_ctx->roc_state); @@ -808,10 +816,12 @@ QDF_STATUS p2p_process_cleanup_roc_queue( roc_ctx = qdf_container_of(p_node, struct p2p_roc_context, node); - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, " + "scan_id:%d, tx ctx:%pK, freq:%d, phy_mode:%d, " + "duration:%d, roc_type:%d, roc_state:%d", roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, roc_ctx->scan_id, - roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->tx_ctx, roc_ctx->chan_freq, roc_ctx->phy_mode, roc_ctx->duration, roc_ctx->roc_type, roc_ctx->roc_state); status = qdf_list_peek_next(&p2p_soc_obj->roc_q, @@ -836,9 +846,11 @@ QDF_STATUS p2p_process_cleanup_roc_queue( roc_ctx = qdf_container_of(p_node, struct p2p_roc_context, node); - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, " + "scan_id:%d, tx ctx:%pK, freq:%d, phy_mode:%d, " + "duration:%d, roc_type:%d, roc_state:%d", roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, - roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan_freq, roc_ctx->phy_mode, roc_ctx->duration, roc_ctx->roc_type, roc_ctx->roc_state); @@ -871,11 +883,13 @@ QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx) p2p_soc_obj = roc_ctx->p2p_soc_obj; - p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx_ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", - p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, - roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, - roc_ctx->phy_mode, roc_ctx->duration, - roc_ctx->roc_type, roc_ctx->roc_state); + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, " + "tx_ctx:%pK, freq:%d, phy_mode:%d, duration:%d, " + "roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan_freq, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); status = qdf_list_insert_back(&p2p_soc_obj->roc_q, &roc_ctx->node); diff --git a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h index 143e01726a8f..b800b20174b4 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h +++ b/drivers/staging/qcacld-3.0/components/p2p/core/src/wlan_p2p_roc.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -81,7 +81,7 @@ enum roc_state { * @vdev_id: Vdev id on which this request has come * @scan_id: Scan id given by scan component for this roc req * @tx_ctx: TX context if this ROC is for tx MGMT - * @chan: Chan for which this RoC has been requested + * @chan_freq: Chan frequency for which this RoC has been requested * @phy_mode: PHY mode * @duration: Duration for the RoC * @roc_type: RoC type User requested or internal @@ -95,7 +95,7 @@ struct p2p_roc_context { uint32_t vdev_id; uint32_t scan_id; void *tx_ctx; - uint8_t chan; + qdf_freq_t chan_freq; uint8_t phy_mode; uint32_t duration; enum roc_type roc_type; @@ -165,9 +165,9 @@ struct p2p_roc_context *p2p_find_roc_by_tx_ctx( struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie); /** - * p2p_find_roc_by_chan() - Find out roc context by channel + * p2p_find_roc_by_chan_freq() - Find out roc context by channel * @p2p_soc_obj: p2p psoc private object - * @chan: channel of the ROC + * @chan_freq: channel frequency of the ROC * * This function finds out roc context by channel from p2p psoc * private object @@ -175,8 +175,8 @@ struct p2p_roc_context *p2p_find_roc_by_tx_ctx( * Return: Pointer to roc context - success * NULL - failure */ -struct p2p_roc_context *p2p_find_roc_by_chan( - struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan); +struct p2p_roc_context *p2p_find_roc_by_chan_freq( + struct p2p_soc_priv_obj *p2p_soc_obj, qdf_freq_t chan_freq); /** * p2p_restart_roc_timer() - Restarts roc timer diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h index 33fd71c70b40..060f0df3ba8b 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -61,13 +62,13 @@ struct p2p_ps_params { /** * struct p2p_roc_req - P2P roc request * @vdev_id: Vdev id on which this request has come - * @chan: Chan for which this RoC has been requested + * @chan_freq: Chan frequency for which this RoC has been requested * @phy_mode: PHY mode * @duration: Duration for the RoC */ struct p2p_roc_req { uint32_t vdev_id; - uint32_t chan; + qdf_freq_t chan_freq; uint32_t phy_mode; uint32_t duration; }; @@ -89,14 +90,14 @@ enum p2p_roc_event { * @vdev_id: Vdev id * @roc_event: RoC event * @cookie: Cookie which is given to supplicant for this roc req - * @chan: Chan for which this RoC has been requested + * @chan_freq: Chan frequency for which this RoC has been requested * @duration: Duration for the RoC */ struct p2p_event { uint32_t vdev_id; enum p2p_roc_event roc_event; uint64_t cookie; - uint32_t chan; + qdf_freq_t chan_freq; uint32_t duration; }; @@ -137,7 +138,7 @@ struct p2p_tx_cnf { /** * struct p2p_mgmt_tx - p2p mgmt tx structure * @vdev_id: Vdev id - * @chan: Chan for which this RoC has been requested + * @chan_freq: Chan frequency for which this RoC has been requested * @wait: Duration for the RoC * @len: Length of tx buffer * @no_cck: Required cck or not @@ -147,7 +148,7 @@ struct p2p_tx_cnf { */ struct p2p_mgmt_tx { uint32_t vdev_id; - uint32_t chan; + qdf_freq_t chan_freq; uint32_t wait; uint32_t len; uint32_t no_cck; diff --git a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c index 50444dc6d8e4..3b862f62d3de 100644 --- a/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c +++ b/drivers/staging/qcacld-3.0/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -118,9 +119,9 @@ QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, QDF_STATUS status; int32_t id; - p2p_debug("soc:%pK, vdev_id:%d, chan:%d, phy_mode:%d, duration:%d", - soc, roc_req->vdev_id, roc_req->chan, - roc_req->phy_mode, roc_req->duration); + p2p_debug("soc:%pK, vdev_id:%d, chanfreq:%d, phy_mode:%d, duration:%d", + soc, roc_req->vdev_id, roc_req->chan_freq, + roc_req->phy_mode, roc_req->duration); if (!soc) { p2p_err("psoc context passed is NULL"); @@ -148,7 +149,7 @@ QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, *cookie = (uint64_t)id; roc_ctx->p2p_soc_obj = p2p_soc_obj; roc_ctx->vdev_id = roc_req->vdev_id; - roc_ctx->chan = roc_req->chan; + roc_ctx->chan_freq = roc_req->chan_freq; roc_ctx->phy_mode = roc_req->phy_mode; roc_ctx->duration = roc_req->duration; roc_ctx->roc_state = ROC_STATE_IDLE; @@ -324,10 +325,11 @@ QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, QDF_STATUS status; int32_t id; - p2p_debug("soc:%pK, vdev_id:%d, chan:%d, wait:%d, buf_len:%d, cck:%d, no ack:%d, off chan:%d", - soc, mgmt_frm->vdev_id, mgmt_frm->chan, - mgmt_frm->wait, mgmt_frm->len, mgmt_frm->no_cck, - mgmt_frm->dont_wait_for_ack, mgmt_frm->off_chan); + p2p_debug("soc:%pK, vdev_id:%d, freq:%d, wait:%d, buf_len:%d," + " cck:%d, no ack:%d, off chan:%d", + soc, mgmt_frm->vdev_id, mgmt_frm->chan_freq, + mgmt_frm->wait, mgmt_frm->len, mgmt_frm->no_cck, + mgmt_frm->dont_wait_for_ack, mgmt_frm->off_chan); if (!soc) { p2p_err("psoc context passed is NULL"); @@ -361,7 +363,7 @@ QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, *cookie = (uint64_t)id; tx_action->p2p_soc_obj = p2p_soc_obj; tx_action->vdev_id = mgmt_frm->vdev_id; - tx_action->chan = mgmt_frm->chan; + tx_action->chan_freq = mgmt_frm->chan_freq; tx_action->duration = mgmt_frm->wait; tx_action->buf_len = mgmt_frm->len; tx_action->no_cck = mgmt_frm->no_cck; diff --git a/drivers/staging/qcacld-3.0/components/target_if/connection_mgr/src/target_if_cm_roam_offload.c b/drivers/staging/qcacld-3.0/components/target_if/connection_mgr/src/target_if_cm_roam_offload.c index 46e1cab26484..970d0429f1b7 100644 --- a/drivers/staging/qcacld-3.0/components/target_if/connection_mgr/src/target_if_cm_roam_offload.c +++ b/drivers/staging/qcacld-3.0/components/target_if/connection_mgr/src/target_if_cm_roam_offload.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -264,6 +265,41 @@ target_if_cm_roam_rssi_diff_6ghz(struct wlan_objmgr_vdev *vdev, return status; } +/** + * target_if_cm_roam_rssi_delta_6ghz_to_non_6ghz() - Sends the roam RSSI + * diff value to the FW. This value is used to determine how much better + * the RSSI of the new/roamable non-6 GHz AP must be for roaming. + * + * @vdev: vdev object + * @roam_rssi_delta_6ghz_to_non_6ghz: RSSI diff value to be used for roaming to + * Non 6 GHz AP + * + * Return: QDF_STATUS + */ +static QDF_STATUS +target_if_cm_roam_rssi_delta_6ghz_to_non_6ghz(struct wlan_objmgr_vdev *vdev, + uint8_t roam_rssi_delta_6ghz_to_non_6ghz) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint8_t vdev_id; + wmi_unified_t wmi_handle; + + wmi_handle = target_if_cm_roam_get_wmi_handle_from_vdev(vdev); + if (!wmi_handle) + return status; + + vdev_id = wlan_vdev_get_id(vdev); + status = target_if_roam_set_param( + wmi_handle, vdev_id, + WMI_ROAM_PARAM_ROAM_RSSI_PENALTY_FOR_NON_6GHZ_CAND_AP, + roam_rssi_delta_6ghz_to_non_6ghz); + + if (QDF_IS_STATUS_ERROR(status)) + target_if_err("Failed to set WMI_ROAM_PARAM_ROAM_RSSI_PENALTY_FOR_NON_6GHZ_CAND_AP"); + + return status; +} + static QDF_STATUS target_if_cm_roam_scan_offload_rssi_thresh( wmi_unified_t wmi_handle, @@ -367,6 +403,13 @@ target_if_cm_roam_rssi_diff_6ghz(struct wlan_objmgr_vdev *vdev, return QDF_STATUS_E_NOSUPPORT; } +static QDF_STATUS +target_if_cm_roam_rssi_delta_6ghz_to_non_6ghz(struct wlan_objmgr_vdev *vdev, + uint8_t roam_rssi_diff_6ghz) +{ + return QDF_STATUS_E_NOSUPPORT; +} + static inline void target_if_check_hi_rssi_5ghz_support( wmi_unified_t wmi_handle, @@ -1276,6 +1319,9 @@ target_if_cm_roam_send_start(struct wlan_objmgr_vdev *vdev, if (req->wlan_roam_rssi_diff_6ghz) target_if_cm_roam_rssi_diff_6ghz(vdev, req->wlan_roam_rssi_diff_6ghz); + if (req->wlan_roam_rssi_delta_6ghz_to_non_6ghz) + target_if_cm_roam_rssi_delta_6ghz_to_non_6ghz( + vdev, req->wlan_roam_rssi_delta_6ghz_to_non_6ghz); /* add other wmi commands */ end: @@ -1516,6 +1562,10 @@ target_if_cm_roam_send_update_config(struct wlan_objmgr_vdev *vdev, if (req->wlan_roam_rssi_diff_6ghz) target_if_cm_roam_rssi_diff_6ghz( vdev, req->wlan_roam_rssi_diff_6ghz); + + if (req->wlan_roam_rssi_delta_6ghz_to_non_6ghz) + target_if_cm_roam_rssi_delta_6ghz_to_non_6ghz( + vdev, req->wlan_roam_rssi_delta_6ghz_to_non_6ghz); } end: return status; diff --git a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c index 647f5e0229b2..8f5e5272d77c 100644 --- a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c +++ b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c @@ -629,6 +629,10 @@ cm_roam_start_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, wlan_cm_roam_cfg_get_value(psoc, vdev_id, ROAM_RSSI_DIFF_6GHZ, &temp); start_req->wlan_roam_rssi_diff_6ghz = temp.uint_value; + wlan_cm_roam_cfg_get_value(psoc, vdev_id, + ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ, &temp); + start_req->wlan_roam_rssi_delta_6ghz_to_non_6ghz = temp.uint_value; + status = wlan_cm_tgt_send_roam_start_req(psoc, vdev_id, start_req); if (QDF_IS_STATUS_ERROR(status)) mlme_debug("fail to send roam start"); @@ -691,6 +695,10 @@ cm_roam_update_config_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, wlan_cm_roam_cfg_get_value(psoc, vdev_id, ROAM_RSSI_DIFF_6GHZ, &temp); update_req->wlan_roam_rssi_diff_6ghz = temp.uint_value; + wlan_cm_roam_cfg_get_value(psoc, vdev_id, + ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ, &temp); + update_req->wlan_roam_rssi_delta_6ghz_to_non_6ghz = temp.uint_value; + status = wlan_cm_tgt_send_roam_update_req(psoc, vdev_id, update_req); if (QDF_IS_STATUS_ERROR(status)) mlme_debug("fail to send update config"); diff --git a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h index 9a027bc6a11f..b4d17e9c04fe 100644 --- a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h +++ b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -120,12 +121,14 @@ * @BEACON_RSSI_WEIGHT: Beacon Rssi weight parameter * @HI_RSSI_DELAY_BTW_SCANS: High Rssi delay between scans * @ROAM_RSSI_DIFF_6GHZ: roam rssi diff for 6 GHz AP + * @ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ: roam rssi diff for Non 6 GHz AP */ enum roam_cfg_param { RSSI_CHANGE_THRESHOLD, BEACON_RSSI_WEIGHT, HI_RSSI_DELAY_BTW_SCANS, ROAM_RSSI_DIFF_6GHZ, + ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ, }; /** @@ -1206,6 +1209,8 @@ enum roam_rt_stats_params { * scan only on prior discovery of any 6 GHz support in the environment. * @wlan_roam_rssi_diff_6ghz: This value is used as to how better the RSSI of * the new/roamable 6GHz AP should be for roaming. + * @wlan_roam_rssi_delta_6ghz_to_non_6ghz: This value is used as to how better + * the RSSI of the new/roamable non 6GHz AP should be for roaming. */ struct wlan_roam_start_config { struct wlan_roam_offload_scan_rssi_params rssi_params; @@ -1229,6 +1234,7 @@ struct wlan_roam_start_config { uint8_t wlan_exclude_rm_partial_scan_freq; uint8_t wlan_roam_full_scan_6ghz_on_disc; uint8_t wlan_roam_rssi_diff_6ghz; + uint8_t wlan_roam_rssi_delta_6ghz_to_non_6ghz; /* other wmi cmd structures */ }; @@ -1281,6 +1287,8 @@ struct wlan_roam_stop_config { * scan only on prior discovery of any 6 GHz support in the environment. * @wlan_roam_rssi_diff_6ghz: This value is used as to how better the RSSI of * the new/roamable 6GHz AP should be for roaming. + * @wlan_roam_rssi_delta_6ghz_to_non_6ghz: This value is used as to how better + * the RSSI of the new/roamable non 6GHz AP should be for roaming. */ struct wlan_roam_update_config { struct wlan_roam_beacon_miss_cnt beacon_miss_cnt; @@ -1299,6 +1307,7 @@ struct wlan_roam_update_config { uint8_t wlan_exclude_rm_partial_scan_freq; uint8_t wlan_roam_full_scan_6ghz_on_disc; uint8_t wlan_roam_rssi_diff_6ghz; + uint8_t wlan_roam_rssi_delta_6ghz_to_non_6ghz; }; #if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD) @@ -1522,6 +1531,8 @@ enum roam_fail_params { * @roam_invoke_fail_reason: One of reason id from enum * wmi_roam_invoke_status_error in case of forced roam * @roam_rssi_diff_6ghz: roam rssi diff for 6 GHz AP + * @roam_rssi_delta_6ghz_to_non_6ghz: RSSI Delta value to be used for roaming + * from 6 GHz to Non 6GHz AP. */ struct wlan_cm_rso_configs { uint8_t rescan_rssi_delta; @@ -1532,6 +1543,7 @@ struct wlan_cm_rso_configs { uint32_t roam_trigger_reason; uint32_t roam_invoke_fail_reason; uint8_t roam_rssi_diff_6ghz; + uint8_t roam_rssi_delta_6ghz_to_non_6ghz; }; /** diff --git a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_roam_api.c b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_roam_api.c index 53fce94e29c3..8ad4cef25e38 100644 --- a/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_roam_api.c +++ b/drivers/staging/qcacld-3.0/components/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_roam_api.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -519,6 +520,10 @@ QDF_STATUS wlan_cm_roam_cfg_get_value(struct wlan_objmgr_psoc *psoc, case ROAM_RSSI_DIFF_6GHZ: dst_config->uint_value = src_config->roam_rssi_diff_6ghz; break; + case ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ: + dst_config->uint_value = + src_config->roam_rssi_delta_6ghz_to_non_6ghz; + break; default: mlme_err("Invalid roam config requested:%d", roam_cfg_type); status = QDF_STATUS_E_FAILURE; @@ -568,6 +573,10 @@ wlan_cm_roam_cfg_set_value(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id, case ROAM_RSSI_DIFF_6GHZ: dst_config->roam_rssi_diff_6ghz = src_config->uint_value; break; + case ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ: + dst_config->roam_rssi_delta_6ghz_to_non_6ghz = + src_config->uint_value; + break; default: mlme_err("Invalid roam config requested:%d", roam_cfg_type); status = QDF_STATUS_E_FAILURE; diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c index 4743ce81ba2a..d8aee3c9ee21 100644 --- a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c +++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_p2p.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -286,15 +286,16 @@ wlan_hdd_validate_and_override_offchan(struct hdd_adapter *adapter, struct ieee80211_channel *chan, bool *offchan) { - uint8_t home_ch; + qdf_freq_t home_ch_freq; if (!offchan || !chan || !(*offchan)) return; - home_ch = hdd_get_adapter_home_channel(adapter); + home_ch_freq = hdd_get_adapter_home_channel(adapter); - if (ieee80211_frequency_to_channel(chan->center_freq) == home_ch) { - hdd_debug("override offchan to 0 at home channel %d", home_ch); + if (chan->center_freq == home_ch_freq) { + hdd_debug("override offchan to 0 at home channel %d", + home_ch_freq); *offchan = false; } } diff --git a/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h b/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h index c8f76c9a8572..500f1f71f75c 100644 --- a/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h +++ b/drivers/staging/qcacld-3.0/core/mac/inc/qwlan_version.h @@ -32,9 +32,9 @@ #define QWLAN_VERSION_MAJOR 2 #define QWLAN_VERSION_MINOR 0 #define QWLAN_VERSION_PATCH 8 -#define QWLAN_VERSION_EXTRA "Z" -#define QWLAN_VERSION_BUILD 34 +#define QWLAN_VERSION_EXTRA "G" +#define QWLAN_VERSION_BUILD 35 -#define QWLAN_VERSIONSTR "2.0.8.34Z" +#define QWLAN_VERSIONSTR "2.0.8.35G" #endif /* QWLAN_VERSION_H */ diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h index 5488a180a145..25f2f90143c5 100644 --- a/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/include/lim_session.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -140,6 +140,7 @@ struct obss_detection_cfg { * @prev_auth_seq_num: Sequence number of previously received auth frame to * detect duplicate frames. * @prev_auth_mac_addr: mac_addr of the sta correspond to @prev_auth_seq_num + * @cal_tpc_post_csa: Recalculate tx power power csa */ struct pe_session { /* To check session table is in use or free */ @@ -580,6 +581,7 @@ struct pe_session { uint32_t dfs_regdomain; uint8_t ap_defined_power_type_6g; uint8_t best_6g_power_type; + bool cal_tpc_post_csa; }; /*------------------------------------------------------------------------- diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c index 49113c9529e4..0f57abe3c224 100644 --- a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_process_sme_req_messages.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1903,6 +1903,8 @@ void lim_parse_tpe_ie(struct mac_context *mac, struct pe_session *session, uint16_t bw_val, ch_width; qdf_freq_t curr_op_freq, curr_freq; enum reg_6g_client_type client_mobility_type; + enum reg_6g_ap_type ap_power_type_6g; + uint16_t reg_max = 0, psd_power = 0; struct ch_params ch_params = {0}; tDot11fIEtransmit_power_env single_tpe; /* @@ -1979,6 +1981,25 @@ void lim_parse_tpe_ie(struct mac_context *mac, struct pe_session *session, curr_op_freq = session->curr_op_freq; bw_val = wlan_reg_get_bw_value(session->ch_width); + if (psd_set) { + if (wlan_reg_is_6ghz_chan_freq(curr_op_freq)) { + ap_power_type_6g = session->best_6g_power_type; + + wlan_reg_get_client_power_for_connecting_ap( + mac->pdev, ap_power_type_6g, + curr_op_freq, true, ®_max, &psd_power); + + /* If reg rules don't support psd power, ignore PSD + * TPE IE + */ + if (!psd_power) { + pe_debug_rl("reg rule doesn't support psd for %d ap type %d", + curr_op_freq, ap_power_type_6g); + psd_set = false; + } + } + } + if (non_psd_set && !psd_set) { single_tpe = tpe_ies[non_psd_index]; vdev_mlme->reg_tpc_obj.is_psd_power = false; @@ -2059,6 +2080,9 @@ void lim_parse_tpe_ie(struct mac_context *mac, struct pe_session *session, single_tpe = tpe_ies[non_psd_index]; vdev_mlme->reg_tpc_obj.eirp_power = single_tpe.tx_power[single_tpe.max_tx_pwr_count]; + pe_debug("eirp_power %d", vdev_mlme->reg_tpc_obj.eirp_power); + if (!psd_set) + vdev_mlme->reg_tpc_obj.is_psd_power = false; } } @@ -2202,7 +2226,6 @@ void lim_calculate_tpc(struct mac_context *mac, skip_tpe = wlan_mlme_skip_tpe(mac->psoc); } else { is_6ghz_freq = true; - is_psd_power = wlan_reg_is_6g_psd_power(mac->pdev); if (LIM_IS_STA_ROLE(session)) ap_power_type_6g = session->best_6g_power_type; } @@ -2210,6 +2233,7 @@ void lim_calculate_tpc(struct mac_context *mac, if (mlme_obj->reg_tpc_obj.num_pwr_levels) { is_tpe_present = true; num_pwr_levels = mlme_obj->reg_tpc_obj.num_pwr_levels; + is_psd_power = mlme_obj->reg_tpc_obj.is_psd_power; } else { num_pwr_levels = lim_get_num_pwr_levels(is_psd_power, session->ch_width); @@ -2223,13 +2247,21 @@ void lim_calculate_tpc(struct mac_context *mac, ch_params.ch_width = CH_WIDTH_20MHZ; - for (i = 0; i < num_pwr_levels; i++) { + for (i = 0; + i < num_pwr_levels && (ch_params.ch_width != CH_WIDTH_INVALID); + i++) { if (is_tpe_present) { if (is_6ghz_freq) { - wlan_reg_get_client_power_for_connecting_ap( - mac->pdev, ap_power_type_6g, - mlme_obj->reg_tpc_obj.frequency[i], - &is_psd_power, ®_max, &psd_power); + if (is_psd_power) { + wlan_reg_get_client_power_for_connecting_ap( + mac->pdev, ap_power_type_6g, + mlme_obj->reg_tpc_obj.frequency[i], + is_psd_power, ®_max, &psd_power); + } else { + wlan_reg_get_client_power_for_connecting_ap( + mac->pdev, ap_power_type_6g, oper_freq, + is_psd_power, ®_max, &psd_power); + } } } else { /* center frequency calculation */ @@ -2241,18 +2273,18 @@ void lim_calculate_tpc(struct mac_context *mac, mac->pdev, oper_freq, 0, &ch_params); mlme_obj->reg_tpc_obj.frequency[i] = ch_params.mhz_freq_seg0; - ch_params.ch_width = - get_next_higher_bw[ch_params.ch_width]; + if (ch_params.ch_width != CH_WIDTH_INVALID) + ch_params.ch_width = + get_next_higher_bw[ch_params.ch_width]; } if (is_6ghz_freq) { if (LIM_IS_STA_ROLE(session)) { wlan_reg_get_client_power_for_connecting_ap (mac->pdev, ap_power_type_6g, mlme_obj->reg_tpc_obj.frequency[i], - &is_psd_power, ®_max, &psd_power); + is_psd_power, ®_max, &psd_power); } else { - ap_power_type_6g = - wlan_reg_get_cur_6g_ap_pwr_type( + wlan_reg_get_cur_6g_ap_pwr_type( mac->pdev, &ap_power_type_6g); wlan_reg_get_6g_chan_ap_power( @@ -2318,7 +2350,6 @@ void lim_calculate_tpc(struct mac_context *mac, } mlme_obj->reg_tpc_obj.num_pwr_levels = num_pwr_levels; - mlme_obj->reg_tpc_obj.is_psd_power = is_psd_power; mlme_obj->reg_tpc_obj.eirp_power = reg_max; mlme_obj->reg_tpc_obj.power_type_6g = ap_power_type_6g; @@ -6489,6 +6520,9 @@ static void lim_process_set_ie_req(struct mac_context *mac_ctx, uint32_t *msg_bu if (p_ext_cap->interworking_service) p_ext_cap->qos_map = 1; + if (wma_is_mbssid_enabled()) + p_ext_cap->multi_bssid = 1; + extra_ext_cap.num_bytes = lim_compute_ext_cap_ie_length(&extra_ext_cap); send_ie: diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c index 8b6a073ea59e..639fac0c3c4f 100644 --- a/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1884,6 +1885,7 @@ void lim_handle_csa_offload_msg(struct mac_context *mac_ctx, err: qdf_mem_free(csa_params); + session_entry->cal_tpc_post_csa = true; } /*-------------------------------------------------------------------------- diff --git a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c index 3c0914e6c71a..8ffa424d5962 100644 --- a/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c +++ b/drivers/staging/qcacld-3.0/core/mac/src/pe/sch/sch_beacon_process.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -704,8 +704,9 @@ static void __sch_beacon_process_for_session(struct mac_context *mac_ctx, } if ((ap_constraint_change && local_constraint) || - (tpe_change && !skip_tpe)) { + (tpe_change && !skip_tpe) || session->cal_tpc_post_csa) { lim_calculate_tpc(mac_ctx, session); + session->cal_tpc_post_csa = false; if (tx_ops->set_tpc_power) tx_ops->set_tpc_power(mac_ctx->psoc, diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c index 262b4f292c30..4484ed3c5ce8 100644 --- a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_api_roam.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -17422,6 +17422,8 @@ static void csr_cm_update_driver_assoc_ies( MIN_TX_PWR_CAP, MAX_TX_PWR_CAP}; uint8_t max_tx_pwr_cap = 0; uint8_t supp_chan_ie[DOT11F_IE_SUPPCHANNELS_MAX_LEN], supp_chan_ie_len; + struct s_ext_cap *extcap; + uint8_t *ext_cap_ie; static const uint8_t qcn_ie[] = {0x8C, 0xFD, 0xF0, 0x1, QCN_IE_VERSION_SUBATTR_ID, QCN_IE_VERSION_SUBATTR_DATA_LEN, @@ -17433,6 +17435,14 @@ static void csr_cm_update_driver_assoc_ies( qdf_mem_copy(rso_mode_cfg->assoc_ie, session->pAddIEAssoc, rso_mode_cfg->assoc_ie_length); + ext_cap_ie = (uint8_t *)wlan_get_ie_ptr_from_eid(WLAN_ELEMID_XCAPS, + rso_mode_cfg->assoc_ie, + rso_mode_cfg->assoc_ie_length); + if (ext_cap_ie && wma_is_mbssid_enabled()) { + extcap = (struct s_ext_cap *)&ext_cap_ie[2]; + extcap->multi_bssid = 1; + } + if (session->pConnectBssDesc) max_tx_pwr_cap = csr_get_cfg_max_tx_power( mac_ctx, @@ -21586,11 +21596,12 @@ csr_process_roam_sync_callback(struct mac_context *mac_ctx, * eapol. So the session->psk_pmk will be stale in PMKSA cached * SAE/OWE roaming case. */ + akm_type = session->connectedProfile.AuthType; + if (roam_synch_data->authStatus == CSR_ROAM_AUTH_STATUS_AUTHENTICATED || - session->pCurRoamProfile->negotiatedAuthType == - eCSR_AUTH_TYPE_SAE || - session->pCurRoamProfile->negotiatedAuthType == - eCSR_AUTH_TYPE_OWE) { + akm_type == eCSR_AUTH_TYPE_SAE || + akm_type == eCSR_AUTH_TYPE_FT_SAE || + akm_type == eCSR_AUTH_TYPE_OWE) { csr_roam_substate_change(mac_ctx, eCSR_ROAM_SUBSTATE_NONE, session_id); /* @@ -21615,8 +21626,7 @@ csr_process_roam_sync_callback(struct mac_context *mac_ctx, &session->connectedProfile.bssid); sme_debug("Trying to find PMKID for " QDF_MAC_ADDR_FMT " AKM Type:%d", QDF_MAC_ADDR_REF(pmkid_cache->BSSID.bytes), - session->pCurRoamProfile->negotiatedAuthType); - akm_type = session->connectedProfile.AuthType; + akm_type); mdie_present = session->connectedProfile.mdid.mdie_present; if (csr_lookup_pmkid_using_bssid(mac_ctx, session, @@ -21696,6 +21706,8 @@ csr_process_roam_sync_callback(struct mac_context *mac_ctx, qdf_mem_zero(pmksa, sizeof(*pmksa)); qdf_mem_free(pmksa); } + } else { + sme_debug("PMK not received from fw"); } sme_debug("pmkid found for " QDF_MAC_ADDR_FMT " len %d", QDF_MAC_ADDR_REF(pmkid_cache->BSSID.bytes), diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c index e40a9efd179c..ccfa013b89c3 100644 --- a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c +++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_neighbor_roam.c @@ -848,6 +848,9 @@ static void csr_neighbor_roam_info_ctx_init(struct mac_context *mac, wlan_cm_roam_cfg_set_value(mac->psoc, session_id, ROAM_RSSI_DIFF_6GHZ, &src_cfg); + src_cfg.uint_value = mac->mlme_cfg->lfr.roam_rssi_delta_6ghz_to_non_6ghz; + wlan_cm_roam_cfg_set_value(mac->psoc, session_id, + ROAM_RSSI_DELTA_6GHZ_TO_NON_6GHZ, &src_cfg); /* * Now we can clear the preauthDone that * was saved as we are connected afresh diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c index dcd409da821b..2d22d4726167 100644 --- a/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_features.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -2907,6 +2907,9 @@ int wma_wow_wakeup_host_event(void *handle, uint8_t *event, uint32_t len) WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param; WOW_EVENT_INFO_fixed_param *wake_info; + if (!wma || !wma->psoc) + return -EINVAL; + event_param = (WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *)event; if (!event_param) { wma_err("Wake event data is null"); diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c index 4fdc73e85179..66ca5d8947a4 100644 --- a/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c +++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_power.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1171,20 +1171,28 @@ static void wma_update_beacon_noa_ie(struct beacon_info *bcn, /* TODO: Assuming p2p noa ie is last ie in the beacon */ qdf_mem_zero(bcn->noa_ie, (bcn->noa_sub_ie_len + sizeof(struct p2p_ie))); - bcn->len -= (bcn->noa_sub_ie_len + - sizeof(struct p2p_ie)); + if (bcn->len < (bcn->noa_sub_ie_len + + sizeof(struct p2p_ie))) + bcn->len = 0; + else + bcn->len -= (bcn->noa_sub_ie_len + + sizeof(struct p2p_ie)); bcn->noa_ie = NULL; bcn->noa_sub_ie_len = 0; } - wma_debug("No need to update NoA"); return; } if (bcn->noa_sub_ie_len && bcn->noa_ie) { + if (bcn->len < (bcn->noa_sub_ie_len + sizeof(struct p2p_ie))) + bcn->len = 0; + else + bcn->len -= (bcn->noa_sub_ie_len + + sizeof(struct p2p_ie)); + /* NoA present in previous beacon, update it */ wma_debug("NoA present in previous beacon, update the NoA IE, bcn->len %u bcn->noa_sub_ie_len %u", - bcn->len, bcn->noa_sub_ie_len); - bcn->len -= (bcn->noa_sub_ie_len + sizeof(struct p2p_ie)); + bcn->len, bcn->noa_sub_ie_len); qdf_mem_zero(bcn->noa_ie, (bcn->noa_sub_ie_len + sizeof(struct p2p_ie))); } else { /* NoA is not present in previous beacon */ diff --git a/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c b/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c index ddeba7bf0d51..8363fbe52a80 100644 --- a/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c +++ b/drivers/staging/qcacld-3.0/os_if/p2p/src/wlan_cfg80211_p2p.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -281,8 +282,7 @@ static void wlan_p2p_event_callback(void *user_data, goto fail; } - chan = ieee80211_get_channel(wdev->wiphy, - wlan_chan_to_freq(p2p_event->chan)); + chan = ieee80211_get_channel(wdev->wiphy, p2p_event->chan_freq); if (!chan) { osif_err("channel conversion failed"); goto fail; @@ -360,7 +360,7 @@ int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, return -EINVAL; } - roc_req.chan = (uint32_t)wlan_freq_to_chan(chan->center_freq); + roc_req.chan_freq = chan->center_freq; roc_req.duration = duration; roc_req.vdev_id = (uint32_t)vdev_id; @@ -372,7 +372,7 @@ int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, } if (!ok) { - osif_err("channel%d not OK for DNBS", roc_req.chan); + osif_err("channel%d not OK for DNBS", roc_req.chan_freq); return -EINVAL; } @@ -409,7 +409,7 @@ int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, struct p2p_mgmt_tx mgmt_tx = {0}; struct wlan_objmgr_psoc *psoc; uint8_t vdev_id; - uint32_t channel = 0; + qdf_freq_t chan_freq = 0; if (!vdev) { osif_err("invalid vdev object"); @@ -417,7 +417,7 @@ int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, } if (chan) - channel = (uint32_t)wlan_freq_to_chan(chan->center_freq); + chan_freq = chan->center_freq; else osif_debug("NULL chan, set channel to 0"); @@ -436,8 +436,7 @@ int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, int ret; bool ok; - ret = policy_mgr_is_chan_ok_for_dnbs( - psoc, wlan_chan_to_freq(channel), &ok); + ret = policy_mgr_is_chan_ok_for_dnbs(psoc, chan_freq, &ok); if (QDF_IS_STATUS_ERROR(ret)) { osif_err("policy_mgr_is_chan_ok_for_dnbs():ret:%d", ret); @@ -445,13 +444,13 @@ int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, } if (!ok) { osif_err("Rejecting mgmt_tx for channel:%d as DNSC is set", - channel); + chan_freq); return -EINVAL; } } mgmt_tx.vdev_id = (uint32_t)vdev_id; - mgmt_tx.chan = channel; + mgmt_tx.chan_freq = chan_freq; mgmt_tx.wait = wait; mgmt_tx.len = len; mgmt_tx.no_cck = (uint32_t)no_cck; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a12c5c9ed397..2b9a638bbd60 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -491,6 +491,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, dwc3_gadget_ep_get_transfer_index(dep); } + if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER && + !(cmd & DWC3_DEPCMD_CMDIOC)) + mdelay(1); + if (saved_config) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg |= saved_config; @@ -3750,9 +3754,6 @@ int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) else dep->flags |= DWC3_EP_END_TRANSFER_PENDING; - if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) - udelay(100); - return ret; } EXPORT_SYMBOL(dwc3_stop_active_transfer); diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 094a88ff9a67..b4c08b4ed602 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -808,9 +808,9 @@ static struct usb_function_instance *uvc_alloc_inst(void) cd->wObjectiveFocalLengthMax = cpu_to_le16(0); cd->wOcularFocalLength = cpu_to_le16(0); cd->bControlSize = 3; - cd->bmControls[0] = 2; - cd->bmControls[1] = 0; - cd->bmControls[2] = 0; + cd->bmControls[0] = 62; + cd->bmControls[1] = 126; + cd->bmControls[2] = 10; pd = &opts->uvc_processing; pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2); @@ -820,8 +820,8 @@ static struct usb_function_instance *uvc_alloc_inst(void) pd->bSourceID = 1; pd->wMaxMultiplier = cpu_to_le16(16*1024); pd->bControlSize = 2; - pd->bmControls[0] = 1; - pd->bmControls[1] = 0; + pd->bmControls[0] = 91; + pd->bmControls[1] = 23; pd->iProcessing = 0; pd->bmVideoStandards = 0; diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 1473d25ff17a..9961d7d04546 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -65,7 +65,7 @@ extern unsigned int uvc_gadget_trace_param; * Driver specific constants */ -#define UVC_NUM_REQUESTS 4 +#define UVC_NUM_REQUESTS 64 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 00fb58e50a15..5480a2988fe4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -762,16 +762,19 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased" }; enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -1080,6 +1083,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -1190,6 +1194,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -1324,7 +1329,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -1337,12 +1342,32 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_max_video_frame_buffer_size, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -1363,6 +1388,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -1375,6 +1401,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -1383,7 +1412,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -1413,9 +1445,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -1856,6 +1885,260 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { .name = "mjpeg", }; +/* ----------------------------------------------------------------------------- + * streaming/framebased/ + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ + static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = scnprintf(page, PAGE_SIZE, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ + static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = scnprintf(page, PAGE_SIZE, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + + static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + + static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + /* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2001,6 +2284,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2049,6 +2333,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2059,6 +2348,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2069,6 +2363,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -2123,6 +2438,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -2132,8 +2456,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -2145,6 +2472,9 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; } @@ -2357,6 +2687,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 495f0ec663ea..49f1f2ad134e 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -58,6 +58,9 @@ struct uvc_format { static struct uvc_format uvc_formats[] = { { 16, V4L2_PIX_FMT_YUYV }, { 0, V4L2_PIX_FMT_MJPEG }, + { 0, V4L2_PIX_FMT_H264 }, + { 12, V4L2_PIX_FMT_YUV420 }, + { 8, V4L2_PIX_FMT_GREY }, }; static int diff --git a/fs/proc/array.c b/fs/proc/array.c index 46dcb6f0eccf..be8a186d21ad 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -342,6 +342,10 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) seq_put_decimal_ull(m, "NoNewPrivs:\t", task_no_new_privs(p)); #ifdef CONFIG_SECCOMP seq_put_decimal_ull(m, "\nSeccomp:\t", p->seccomp.mode); +#ifdef CONFIG_SECCOMP_FILTER + seq_put_decimal_ull(m, "\nSeccomp_filters:\t", + atomic_read(&p->seccomp.filter_count)); +#endif #endif seq_puts(m, "\nSpeculation_Store_Bypass:\t"); switch (arch_prctl_spec_ctrl_get(p, PR_SPEC_STORE_BYPASS)) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 6cac23a218a5..9143e5f41890 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3330,7 +3330,7 @@ static const struct pid_entry tgid_base_stuff[] = { DIR("task", S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations), DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), DIR("map_files", S_IRUSR|S_IXUSR, proc_map_files_inode_operations, proc_map_files_operations), - DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), + DIR("fdinfo", S_IRUGO|S_IXUGO, proc_fdinfo_inode_operations, proc_fdinfo_operations), DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), #ifdef CONFIG_NET DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), @@ -3750,7 +3750,7 @@ static const struct inode_operations proc_tid_comm_inode_operations = { */ static const struct pid_entry tid_base_stuff[] = { DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), - DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), + DIR("fdinfo", S_IRUGO|S_IXUGO, proc_fdinfo_inode_operations, proc_fdinfo_operations), DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), #ifdef CONFIG_NET DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 81882a13212d..6b634c0a9b6e 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -53,9 +54,10 @@ static int seq_show(struct seq_file *m, void *v) if (ret) return ret; - seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\n", + seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n", (long long)file->f_pos, f_flags, - real_mount(file->f_path.mnt)->mnt_id); + real_mount(file->f_path.mnt)->mnt_id, + file_inode(file)->i_ino); show_fd_locks(m, file, files); if (seq_has_overflowed(m)) @@ -69,8 +71,30 @@ out: return 0; } +static int proc_fdinfo_access_allowed(struct inode *inode) +{ + bool allowed = false; + struct task_struct *task = get_proc_task(inode); + + if (!task) + return -ESRCH; + + allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS); + put_task_struct(task); + + if (!allowed) + return -EACCES; + + return 0; +} + static int seq_fdinfo_open(struct inode *inode, struct file *file) { + int ret = proc_fdinfo_access_allowed(inode); + + if (ret) + return ret; + return single_open(file, seq_show, inode); } @@ -325,7 +349,7 @@ static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry, struct proc_inode *ei; struct inode *inode; - inode = proc_pid_make_inode(dentry->d_sb, task, S_IFREG | S_IRUSR); + inode = proc_pid_make_inode(dentry->d_sb, task, S_IFREG | S_IRUGO); if (!inode) return ERR_PTR(-ENOENT); @@ -351,12 +375,23 @@ static int proc_readfdinfo(struct file *file, struct dir_context *ctx) proc_fdinfo_instantiate); } +static int proc_open_fdinfo(struct inode *inode, struct file *file) +{ + int ret = proc_fdinfo_access_allowed(inode); + + if (ret) + return ret; + + return 0; +} + const struct inode_operations proc_fdinfo_inode_operations = { .lookup = proc_lookupfdinfo, .setattr = proc_setattr, }; const struct file_operations proc_fdinfo_operations = { + .open = proc_open_fdinfo, .read = generic_read_dir, .iterate_shared = proc_readfdinfo, .llseek = generic_file_llseek, diff --git a/include/asm-generic/seccomp.h b/include/asm-generic/seccomp.h index 1321ac7821d7..6b6f42bc58f9 100644 --- a/include/asm-generic/seccomp.h +++ b/include/asm-generic/seccomp.h @@ -33,7 +33,7 @@ static inline const int *get_compat_mode1_syscalls(void) static const int mode1_syscalls_32[] = { __NR_seccomp_read_32, __NR_seccomp_write_32, __NR_seccomp_exit_32, __NR_seccomp_sigreturn_32, - 0, /* null terminated */ + -1, /* negative terminated */ }; return mode1_syscalls_32; } diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 9e2ad3b81690..e06623fa5b80 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -162,5 +162,9 @@ extern br_notify_hook_t __rcu *br_notify_hook; typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, struct sk_buff *skb); extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; +typedef struct net_bridge_port *br_get_dst_hook_t( + const struct net_bridge_port *src, + struct sk_buff **skb); +extern br_get_dst_hook_t __rcu *br_get_dst_hook; #endif #endif diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h index 245f86fbbe46..95b05a85f61c 100644 --- a/include/linux/io-pgtable-fast.h +++ b/include/linux/io-pgtable-fast.h @@ -44,7 +44,7 @@ struct av8l_fast_io_pgtable { int av8l_fast_map_public(struct io_pgtable_ops *ops, unsigned long iova, phys_addr_t paddr, size_t size, int prot); -void av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, +size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size); int av8l_fast_map_sg_public(struct io_pgtable_ops *ops, @@ -63,9 +63,10 @@ av8l_fast_map_public(struct io_pgtable_ops *ops, unsigned long iova, { return -EINVAL; } -static inline void av8l_fast_unmap_public(struct io_pgtable_ops *ops, +static inline size_t av8l_fast_unmap_public(struct io_pgtable_ops *ops, unsigned long iova, size_t size) { + return 0; } static inline int av8l_fast_map_sg_public(struct io_pgtable_ops *ops, diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 6f006ccbdca7..6003273ec2e9 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -33,6 +33,7 @@ struct ipv6_devconf { __s32 max_addresses; __s32 accept_ra_defrtr; __s32 accept_ra_min_hop_limit; + __s32 accept_ra_min_lft; __s32 accept_ra_pinfo; __s32 ignore_routes_with_linkdown; #ifdef CONFIG_IPV6_ROUTER_PREF diff --git a/include/linux/kfence.h b/include/linux/kfence.h new file mode 100644 index 000000000000..3fe6dd8a18c1 --- /dev/null +++ b/include/linux/kfence.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Kernel Electric-Fence (KFENCE). Public interface for allocator and fault + * handler integration. For more info see Documentation/dev-tools/kfence.rst. + * + * Copyright (C) 2020, Google LLC. + */ + +#ifndef _LINUX_KFENCE_H +#define _LINUX_KFENCE_H + +#include +#include + +#ifdef CONFIG_KFENCE + +/* + * We allocate an even number of pages, as it simplifies calculations to map + * address to metadata indices; effectively, the very first page serves as an + * extended guard page, but otherwise has no special purpose. + */ +#define KFENCE_POOL_SIZE ((CONFIG_KFENCE_NUM_OBJECTS + 1) * 2 * PAGE_SIZE) +extern char *__kfence_pool; + +#ifdef CONFIG_KFENCE_STATIC_KEYS +#include +DECLARE_STATIC_KEY_FALSE(kfence_allocation_key); +#else +#include +extern atomic_t kfence_allocation_gate; +#endif + +/** + * is_kfence_address() - check if an address belongs to KFENCE pool + * @addr: address to check + * + * Return: true or false depending on whether the address is within the KFENCE + * object range. + * + * KFENCE objects live in a separate page range and are not to be intermixed + * with regular heap objects (e.g. KFENCE objects must never be added to the + * allocator freelists). Failing to do so may and will result in heap + * corruptions, therefore is_kfence_address() must be used to check whether + * an object requires specific handling. + * + * Note: This function may be used in fast-paths, and is performance critical. + * Future changes should take this into account; for instance, we want to avoid + * introducing another load and therefore need to keep KFENCE_POOL_SIZE a + * constant (until immediate patching support is added to the kernel). + */ +static __always_inline bool is_kfence_address(const void *addr) +{ + /* + * The __kfence_pool != NULL check is required to deal with the case + * where __kfence_pool == NULL && addr < KFENCE_POOL_SIZE. Keep it in + * the slow-path after the range-check! + */ + return unlikely((unsigned long)((char *)addr - __kfence_pool) < KFENCE_POOL_SIZE && __kfence_pool); +} + +/** + * kfence_alloc_pool() - allocate the KFENCE pool via memblock + */ +void __init kfence_alloc_pool(void); + +/** + * kfence_init() - perform KFENCE initialization at boot time + * + * Requires that kfence_alloc_pool() was called before. This sets up the + * allocation gate timer, and requires that workqueues are available. + */ +void __init kfence_init(void); + +/** + * kfence_shutdown_cache() - handle shutdown_cache() for KFENCE objects + * @s: cache being shut down + * + * Before shutting down a cache, one must ensure there are no remaining objects + * allocated from it. Because KFENCE objects are not referenced from the cache + * directly, we need to check them here. + * + * Note that shutdown_cache() is internal to SL*B, and kmem_cache_destroy() does + * not return if allocated objects still exist: it prints an error message and + * simply aborts destruction of a cache, leaking memory. + * + * If the only such objects are KFENCE objects, we will not leak the entire + * cache, but instead try to provide more useful debug info by making allocated + * objects "zombie allocations". Objects may then still be used or freed (which + * is handled gracefully), but usage will result in showing KFENCE error reports + * which include stack traces to the user of the object, the original allocation + * site, and caller to shutdown_cache(). + */ +void kfence_shutdown_cache(struct kmem_cache *s); + +/* + * Allocate a KFENCE object. Allocators must not call this function directly, + * use kfence_alloc() instead. + */ +void *__kfence_alloc(struct kmem_cache *s, size_t size, gfp_t flags); + +/** + * kfence_alloc() - allocate a KFENCE object with a low probability + * @s: struct kmem_cache with object requirements + * @size: exact size of the object to allocate (can be less than @s->size + * e.g. for kmalloc caches) + * @flags: GFP flags + * + * Return: + * * NULL - must proceed with allocating as usual, + * * non-NULL - pointer to a KFENCE object. + * + * kfence_alloc() should be inserted into the heap allocation fast path, + * allowing it to transparently return KFENCE-allocated objects with a low + * probability using a static branch (the probability is controlled by the + * kfence.sample_interval boot parameter). + */ +static __always_inline void *kfence_alloc(struct kmem_cache *s, size_t size, gfp_t flags) +{ +#ifdef CONFIG_KFENCE_STATIC_KEYS + if (static_branch_unlikely(&kfence_allocation_key)) +#else + if (unlikely(!atomic_read(&kfence_allocation_gate))) +#endif + return __kfence_alloc(s, size, flags); + return NULL; +} + +/** + * kfence_ksize() - get actual amount of memory allocated for a KFENCE object + * @addr: pointer to a heap object + * + * Return: + * * 0 - not a KFENCE object, must call __ksize() instead, + * * non-0 - this many bytes can be accessed without causing a memory error. + * + * kfence_ksize() returns the number of bytes requested for a KFENCE object at + * allocation time. This number may be less than the object size of the + * corresponding struct kmem_cache. + */ +size_t kfence_ksize(const void *addr); + +/** + * kfence_object_start() - find the beginning of a KFENCE object + * @addr: address within a KFENCE-allocated object + * + * Return: address of the beginning of the object. + * + * SL[AU]B-allocated objects are laid out within a page one by one, so it is + * easy to calculate the beginning of an object given a pointer inside it and + * the object size. The same is not true for KFENCE, which places a single + * object at either end of the page. This helper function is used to find the + * beginning of a KFENCE-allocated object. + */ +void *kfence_object_start(const void *addr); + +/** + * __kfence_free() - release a KFENCE heap object to KFENCE pool + * @addr: object to be freed + * + * Requires: is_kfence_address(addr) + * + * Release a KFENCE object and mark it as freed. + */ +void __kfence_free(void *addr); + +/** + * kfence_free() - try to release an arbitrary heap object to KFENCE pool + * @addr: object to be freed + * + * Return: + * * false - object doesn't belong to KFENCE pool and was ignored, + * * true - object was released to KFENCE pool. + * + * Release a KFENCE object and mark it as freed. May be called on any object, + * even non-KFENCE objects, to simplify integration of the hooks into the + * allocator's free codepath. The allocator must check the return value to + * determine if it was a KFENCE object or not. + */ +static __always_inline __must_check bool kfence_free(void *addr) +{ + if (!is_kfence_address(addr)) + return false; + __kfence_free(addr); + return true; +} + +/** + * kfence_handle_page_fault() - perform page fault handling for KFENCE pages + * @addr: faulting address + * @is_write: is access a write + * @regs: current struct pt_regs (can be NULL, but shows full stack trace) + * + * Return: + * * false - address outside KFENCE pool, + * * true - page fault handled by KFENCE, no additional handling required. + * + * A page fault inside KFENCE pool indicates a memory error, such as an + * out-of-bounds access, a use-after-free or an invalid memory access. In these + * cases KFENCE prints an error message and marks the offending page as + * present, so that the kernel can proceed. + */ +bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs); + +#else /* CONFIG_KFENCE */ + +static inline bool is_kfence_address(const void *addr) { return false; } +static inline void kfence_alloc_pool(void) { } +static inline void kfence_init(void) { } +static inline void kfence_shutdown_cache(struct kmem_cache *s) { } +static inline void *kfence_alloc(struct kmem_cache *s, size_t size, gfp_t flags) { return NULL; } +static inline size_t kfence_ksize(const void *addr) { return 0; } +static inline void *kfence_object_start(const void *addr) { return NULL; } +static inline void __kfence_free(void *addr) { } +static inline bool __must_check kfence_free(void *addr) { return false; } +static inline bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write, + struct pt_regs *regs) +{ + return false; +} + +#endif + +#endif /* _LINUX_KFENCE_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 773a335c2c5e..eb40c638684f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2614,6 +2614,8 @@ enum netdev_cmd { NETDEV_CVLAN_FILTER_DROP_INFO, NETDEV_SVLAN_FILTER_PUSH_INFO, NETDEV_SVLAN_FILTER_DROP_INFO, + NETDEV_BR_JOIN, + NETDEV_BR_LEAVE, }; const char *netdev_cmd_to_name(enum netdev_cmd cmd); diff --git a/include/linux/random.h b/include/linux/random.h index 8ed2e245d51e..7e742ef51a83 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -64,7 +64,8 @@ static inline unsigned long get_random_canary(void) return get_random_long() & CANARY_MASK; } -int __init random_init(const char *command_line); +void __init random_init_early(const char *command_line); +void __init random_init(void); bool rng_is_initialized(void); int wait_for_random_bytes(void); int register_random_ready_notifier(struct notifier_block *nb); diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 84868d37b35d..a6f90d9cea47 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -12,6 +12,7 @@ #ifdef CONFIG_SECCOMP #include +#include #include struct seccomp_filter; @@ -28,6 +29,7 @@ struct seccomp_filter; */ struct seccomp { int mode; + atomic_t filter_count; struct seccomp_filter *filter; }; @@ -81,10 +83,10 @@ static inline int seccomp_mode(struct seccomp *s) #endif /* CONFIG_SECCOMP */ #ifdef CONFIG_SECCOMP_FILTER -extern void put_seccomp_filter(struct task_struct *tsk); +extern void seccomp_filter_release(struct task_struct *tsk); extern void get_seccomp_filter(struct task_struct *tsk); #else /* CONFIG_SECCOMP_FILTER */ -static inline void put_seccomp_filter(struct task_struct *tsk) +static inline void seccomp_filter_release(struct task_struct *tsk) { return; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 120a2162b844..add201a31f2c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1626,6 +1626,22 @@ static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) return 0; } +/* This variant of skb_unclone() makes sure skb->truesize is not changed */ +static inline int skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri) +{ + might_sleep_if(gfpflags_allow_blocking(pri)); + + if (skb_cloned(skb)) { + unsigned int save = skb->truesize; + int res; + + res = pskb_expand_head(skb, 0, 0, pri); + skb->truesize = save; + return res; + } + return 0; +} + /** * skb_header_cloned - is the header a clone * @skb: buffer to check diff --git a/include/net/cnss.h b/include/net/cnss.h index 9c99bdc2032d..77bc3157c2aa 100644 --- a/include/net/cnss.h +++ b/include/net/cnss.h @@ -10,7 +10,6 @@ #include #include -#ifdef CONFIG_CNSS #define MAX_FIRMWARE_SIZE (1 * 1024 * 1024) #define CNSS_MAX_FILE_NAME 20 #define PINCTRL_SLEEP 0 @@ -177,7 +176,6 @@ int cnss_pm_runtime_request(struct device *dev, enum cnss_runtime_request request); void cnss_set_cc_source(enum cnss_cc_src cc_source); enum cnss_cc_src cnss_get_cc_source(void); -#endif void cnss_pm_wake_lock_init(struct wakeup_source **ws, const char *name); void cnss_pm_wake_lock(struct wakeup_source *ws); diff --git a/include/trace/events/error_report.h b/include/trace/events/error_report.h new file mode 100644 index 000000000000..96f64bf218b2 --- /dev/null +++ b/include/trace/events/error_report.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Declarations for error reporting tracepoints. + * + * Copyright (C) 2021, Google LLC. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM error_report + +#if !defined(_TRACE_ERROR_REPORT_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ERROR_REPORT_H + +#include + +#ifndef __ERROR_REPORT_DECLARE_TRACE_ENUMS_ONCE_ONLY +#define __ERROR_REPORT_DECLARE_TRACE_ENUMS_ONCE_ONLY + +enum error_detector { + ERROR_DETECTOR_KFENCE, + ERROR_DETECTOR_KASAN +}; + +#endif /* __ERROR_REPORT_DECLARE_TRACE_ENUMS_ONCE_ONLY */ + +#define error_detector_list \ + EM(ERROR_DETECTOR_KFENCE, "kfence") \ + EMe(ERROR_DETECTOR_KASAN, "kasan") +/* Always end the list with an EMe. */ + +#undef EM +#undef EMe + +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +error_detector_list + +#undef EM +#undef EMe + +#define EM(a, b) { a, b }, +#define EMe(a, b) { a, b } + +#define show_error_detector_list(val) \ + __print_symbolic(val, error_detector_list) + +DECLARE_EVENT_CLASS(error_report_template, + TP_PROTO(enum error_detector error_detector, unsigned long id), + TP_ARGS(error_detector, id), + TP_STRUCT__entry(__field(enum error_detector, error_detector) + __field(unsigned long, id)), + TP_fast_assign(__entry->error_detector = error_detector; + __entry->id = id;), + TP_printk("[%s] %lx", + show_error_detector_list(__entry->error_detector), + __entry->id)); + +/** + * error_report_end - called after printing the error report + * @error_detector: short string describing the error detection tool + * @id: pseudo-unique descriptor identifying the report + * (e.g. the memory access address) + * + * This event occurs right after a debugging tool finishes printing the error + * report. + */ +DEFINE_EVENT(error_report_template, error_report_end, + TP_PROTO(enum error_detector error_detector, unsigned long id), + TP_ARGS(error_detector, id)); + +#endif /* _TRACE_ERROR_REPORT_H */ + +/* This part must be outside protection */ +#include diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 77f7c1638eb1..645606824258 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -119,6 +119,8 @@ #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_NETNS_COOKIE 71 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__)) diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 9c0f4a92bcff..45406a092e4e 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -187,6 +187,7 @@ enum { DEVCONF_DISABLE_POLICY, DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_NDISC_TCLASS, + DEVCONF_ACCEPT_RA_MIN_LFT, DEVCONF_MAX }; diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 8d2bb198379d..5f63c7b96479 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _UAPI_MSM_IPA_H_ @@ -152,8 +152,9 @@ #define IPA_IOCTL_SET_EXT_ROUTER_MODE 95 #define IPA_IOCTL_ADD_DEL_DSCP_PCP_MAPPING 96 #define IPA_IOCTL_SEND_VLAN_MUXID_MAPPING 97 -#define IPA_IOCTL_SEND_TUNNEL_TEMPLATE_INFO 98 -#define IPA_IOCTL_QUERY_TUNNEL_FEATURE 99 +#define IPA_IOCTL_SEND_TUNNEL_TEMPLATE_INFO 98 +#define IPA_IOCTL_QUERY_TUNNEL_FEATURE 99 +#define IPA_IOCTL_ADD_IPOGRE_MAPPING 100 /** * max size of the header to be inserted */ @@ -976,8 +977,12 @@ enum ipa_eth_pdu_evt { #define IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX }; - -#define IPA_EVENT_MAX_NUM (IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX) +enum ipa_ipogre_event { + IPA_IPOGRE_NOTIFY_EVENT = IPA_ENABLE_ETH_PDU_MODE_EVENT_MAX, + IPA_IPOGRE_EVENT_MAX +#define IPA_IPOGRE_EVENT_MAX IPA_IPOGRE_EVENT_MAX +}; +#define IPA_EVENT_MAX_NUM (IPA_IPOGRE_EVENT_MAX) #define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM) /** @@ -1140,48 +1145,6 @@ static inline const char *exception_type_as_str(enum ipa_exception_type t) "???"; } -/** - * Macro ipa_exception_type_pppoe - * - * This macro is for describing which field is to be looked at for - * exception path consideration. - * - * NOTE 1: The field implies an offset into the packet under - * consideration. This offset will be calculated on behalf of - * the user of this API. - * - * NOTE 2: When exceptions are generated/sent in an ipa_exception - * structure, they will considered to be from the upload - * perspective. And when appropriate, a corresponding, and - * perhaps inverted, downlink exception will be automatically - * created on the callers behalf. As an example: If a - * FIELD_UDP_SRC_PORT is sent, an uplink exception will be - * created for udp source port, and a corresponding - * FIELD_UDP_DST_PORT will be automatically created for the - * downlink. - */ -#define FIELD_IP_PROTOCOL_PPPOE (FIELD_ETHER_TYPE + 1) -#define FIELD_TCP_SRC_PORT_PPPOE (FIELD_IP_PROTOCOL_PPPOE + 1) -#define FIELD_TCP_DST_PORT_PPPOE (FIELD_TCP_SRC_PORT_PPPOE + 1) -#define FIELD_UDP_SRC_PORT_PPPOE (FIELD_TCP_DST_PORT_PPPOE + 1) -#define FIELD_UDP_DST_PORT_PPPOE (FIELD_UDP_SRC_PORT_PPPOE + 1) -#define FIELD_ETHER_TYPE_PPPOE (FIELD_UDP_DST_PORT_PPPOE + 1) -#define FIELD_PPPOE_MAX (FIELD_ETHER_TYPE_PPPOE + 1) - -/* Function to read PPPoE exception in string format */ -static inline const char *pppoe_exception_type_as_str(uint32_t t) -{ - return - (t == FIELD_IP_PROTOCOL_PPPOE) ? "pppoe_ip_protocol" : - (t == FIELD_TCP_SRC_PORT_PPPOE) ? "pppoe_tcp_src_port" : - (t == FIELD_TCP_DST_PORT_PPPOE) ? "pppoe_tcp_dst_port" : - (t == FIELD_UDP_SRC_PORT_PPPOE) ? "pppoe_udp_src_port" : - (t == FIELD_UDP_DST_PORT_PPPOE) ? "pppoe_udp_dst_port" : - (t == FIELD_ETHER_TYPE_PPPOE) ? "pppoe_ether_type" : - (t == FIELD_PPPOE_MAX) ? "pppoe_max" : - "???"; -} - #define IP_TYPE_EXCEPTION(x) \ ((x) == FIELD_IP_PROTOCOL || \ (x) == FIELD_TCP_SRC_PORT || \ @@ -1262,7 +1225,6 @@ struct ipa_field_val_equation_gen { * @payload_length: Payload length. * @ext_attrib_mask: Extended attributes. * @l2tp_udp_next_hdr: next header in L2TP tunneling - * @p_exception : exception to enable for mpls-pppoe * @field_val_equ: for finding a value at a particular offset */ struct ipa_rule_attrib { @@ -1308,7 +1270,7 @@ struct ipa_rule_attrib { __u16 payload_length; __u32 ext_attrib_mask; __u8 l2tp_udp_next_hdr; - __u8 p_exception; + __u8 padding1; struct ipa_field_val_equation_gen fld_val_eq; }; @@ -1594,9 +1556,11 @@ enum ipa_hdr_proc_type { IPA_HDR_PROC_EoGRE_HEADER_REMOVE, IPA_HDR_PROC_WWAN_TO_ETHII_EX, IPA_HDR_PROC_GRE_HEADER_ADD, - IPA_HDR_PROC_GRE_HEADER_REMOVE + IPA_HDR_PROC_GRE_HEADER_REMOVE, + IPA_HDR_PROC_IPOGRE_HEADER_ADD, + IPA_HDR_PROC_IPOGRE_HEADER_REMOVE }; -#define IPA_HDR_PROC_MAX (IPA_HDR_PROC_GRE_HEADER_REMOVE + 1) +#define IPA_HDR_PROC_MAX (IPA_HDR_PROC_IPOGRE_HEADER_REMOVE + 1) /** * struct ipa_rt_rule - attributes of a routing rule @@ -2001,6 +1965,68 @@ struct ipa_ioc_eogre_info { struct IpaDscpVlanPcpMap_t map_info; }; +#define MAX_FLOW_PER_IPOGRE_TUNNEL 10 + +/** + * struct ipa_ipogre_info - + * @ipv4_src: Specifies source v4 address if GRE tunnel is ipv4 + * @ipv4_dst: Specifies destination v4 address if GRE tunnel is ipv4 + * @ipv6_src: Specifies source v6 address if GRE tunnel is ipv6 + * @ipv6_dst: Specifies destination v6 address if GRE tunnel is ipv6 + * @iptype: Specifies GRE tunnel's ip address type + * @tunnel_id: Specifies tunnel id + */ + +struct ipa_ipogre_tunnel_info { + uint32_t ipv4_src; + uint32_t ipv4_dst; + uint32_t ipv6_src[4]; + uint32_t ipv6_dst[4]; + enum ipa_ip_type iptype; + uint8_t tunnel_id; +} __packed; + +/** + * struct ipa_ipogre_info - + * @ipv4_src: Specifies source v4 address if GRE tunnel is ipv4 + * @ipv4_src_subnet: Specifies source v4 address subnet if GRE tunnel is ipv4 + * @ipv4_dst: Specifies destination v4 address if GRE tunnel is ipv4 + * @ipv4_dst_subnet: Specifies destination v4 address subnet if GRE tunnel is ipv4 + * @ipv6_src: Specifies source v6 address if GRE tunnel is ipv6 + * @ipv6_src_subnet: Specifies source v6 address subnet if GRE tunnel is ipv6 + * @ipv6_dst: Specifies destination v6 address if GRE tunnel is ipv6 + * @ipv6_dst_subnet: Specifies destination v6 address subnet if GRE tunnel is ipv6 + * @iptype: Specifies GRE tunnel's ip address type + * @protocol: Specifies protocol of the data traffic + */ + +struct ipa_ipogre_flow_info { + uint32_t ipv4_src; + uint32_t ipv4_src_subnet; + uint32_t ipv4_dst; + uint32_t ipv4_dst_subnet; + uint32_t ipv6_src[4]; + uint32_t ipv6_dst[4]; + uint32_t src_port; + uint32_t dst_port; + enum ipa_ip_type iptype; + uint8_t protocol; + uint8_t ipv6_src_subnet; + uint8_t ipv6_dst_subnet; +} __packed; + +/** + * struct ipa_ipogre_info - + * @ipogre_tunnel_info: Specifies tunnel information + * @ipogre_flow_info: Specifies flows to be offloaded + * @ipa_ipogre_num_flow: Specifies number of flow to be offloaded + */ + +struct ipa_ioc_ipogre_info { + struct ipa_ipogre_tunnel_info ipogre_tunnel_info; + struct ipa_ipogre_flow_info ipogre_flow_info[MAX_FLOW_PER_IPOGRE_TUNNEL]; + uint8_t ipa_ipogre_num_flow; +}; /** * struct ipa_eogre_header_add_procparams - * @eth_hdr_retained: Specifies if Ethernet header is retained or not @@ -2092,6 +2118,45 @@ struct ipa_gre_hdr_proc_ctx_params { struct ipa_gre_header_remove_procparams hdr_remove_param; }; +/** + * struct ipa_ipogre_header_add_procparams - + * @input_ip_version: Specifies if Input header is IPV4(0) or IPV6(1) + * @output_ip_version: Specifies if template header's outer IP is IPV4(0) + * or IPV6(1) + * @Tunnel_Id: Tunnel id associated with the header. + * @Mux_Id: Specifies mux id associated with the template header + */ +struct ipa_ipogre_header_add_procparams { + uint32_t input_ip_version : 1; + uint32_t output_ip_version : 1; + uint32_t tunnel_id : 4; + uint32_t mux_id : 8; + uint32_t reserved :18; +}; + +/** + * struct ipa_ipogre_header_remove_procparams - + * @hdr_len_remove: Specifies how much (in bytes) of the header needs + * to be removed + * @input_ip_version: Specifies if Input header is IPV4(0) or IPV6(1) + * @Tunnel_Id: Tunnel id associated with the header. + */ +struct ipa_ipogre_header_remove_procparams { + uint32_t hdr_len_remove : 8; + uint32_t input_ip_version : 1; + uint32_t tunnel_id : 4; + uint32_t reserved :19; +}; + +/** + * struct ipa_ipogre_hdr_proc_ctx_params - + * @hdr_add_param: parameters for header add + * @hdr_remove_param: parameters for header remove + */ +struct ipa_ipogre_hdr_proc_ctx_params { + struct ipa_ipogre_header_add_procparams hdr_add_param; + struct ipa_ipogre_header_remove_procparams hdr_remove_param; +}; /** * struct ipa_eth_II_to_eth_II_ex_procparams - * @input_ethhdr_negative_offset: Specifies where the ethernet hdr offset is @@ -2154,6 +2219,7 @@ struct ipa_hdr_proc_ctx_add { struct ipa_eth_II_to_eth_II_ex_procparams generic_params; struct ipa_wwan_to_eth_II_ex_procparams generic_params_v2; struct ipa_gre_hdr_proc_ctx_params gre_params; + struct ipa_ipogre_hdr_proc_ctx_params ipogre_params; }; #define IPA_L2TP_HDR_PROC_SUPPORT @@ -4264,6 +4330,10 @@ struct ipa_ioc_dscp_pcp_map_info { IPA_IOCTL_QUERY_TUNNEL_FEATURE, \ uint8_t) +#define IPA_IOC_ADD_IPoGRE_MAPPING _IOWR(IPA_IOC_MAGIC, \ + IPA_IOCTL_ADD_IPOGRE_MAPPING, \ + struct ipa_ioc_ipogre_info) + /* * unique magic number of the Tethering bridge ioctls */ diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index b5f901af79f0..df1f4711d0b7 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -76,6 +76,35 @@ struct seccomp_notif { struct seccomp_data data; }; +/* + * Valid flags for struct seccomp_notif_resp + * + * Note, the SECCOMP_USER_NOTIF_FLAG_CONTINUE flag must be used with caution! + * If set by the process supervising the syscalls of another process the + * syscall will continue. This is problematic because of an inherent TOCTOU. + * An attacker can exploit the time while the supervised process is waiting on + * a response from the supervising process to rewrite syscall arguments which + * are passed as pointers of the intercepted syscall. + * It should be absolutely clear that this means that the seccomp notifier + * _cannot_ be used to implement a security policy! It should only ever be used + * in scenarios where a more privileged process supervises the syscalls of a + * lesser privileged process to get around kernel-enforced security + * restrictions when the privileged process deems this safe. In other words, + * in order to continue a syscall the supervising process should be sure that + * another security mechanism or the kernel itself will sufficiently block + * syscalls if arguments are rewritten to something unsafe. + * + * Similar precautions should be applied when stacking SECCOMP_RET_USER_NOTIF + * or SECCOMP_RET_TRACE. For SECCOMP_RET_USER_NOTIF filters acting on the + * same syscall, the most recently added filter takes precedence. This means + * that the new SECCOMP_RET_USER_NOTIF filter can override any + * SECCOMP_IOCTL_NOTIF_SEND from earlier filters, essentially allowing all + * such filtered syscalls to be executed by sending the response + * SECCOMP_USER_NOTIF_FLAG_CONTINUE. Note that SECCOMP_RET_TRACE can equally + * be overriden by SECCOMP_USER_NOTIF_FLAG_CONTINUE. + */ +#define SECCOMP_USER_NOTIF_FLAG_CONTINUE BIT(0) + struct seccomp_notif_resp { __u64 id; __s64 val; diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index c58854fb7d94..c79b6049d9d7 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -597,5 +597,63 @@ struct UVC_FRAME_MJPEG(n) { \ __le32 dwFrameInterval[n]; \ } __attribute__ ((packed)) +/* Frame Based Payload - 3.1.1. Frame Based Video Format Descriptor */ +struct uvc_format_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 guidFormat[16]; + __u8 bBitsPerPixel; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; + __u8 bVariableSize; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_FRAMEBASED_SIZE 28 + +/* Frame Based Payload - 3.1.2. Frame Based Video Frame Descriptor */ +struct uvc_frame_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwBytesPerLine; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_FRAMEBASED_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_FRAMEBASED(n) \ + uvc_frame_framebased_##n + +#define DECLARE_UVC_FRAME_FRAMEBASED(n) \ + struct UVC_FRAME_FRAMEBASED(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwBytesPerLine; \ + __u32 dwFrameInterval[n]; \ + } __attribute__ ((packed)) + #endif /* __LINUX_USB_VIDEO_H */ diff --git a/init/Kconfig b/init/Kconfig index 287c4cdf744f..3fe1fd2b9625 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -44,6 +44,18 @@ config CLANG_VERSION int default $(shell,$(srctree)/scripts/clang-version.sh $(CC)) +config AS_IS_GNU + def_bool $(success,test "$(as-name)" = GNU) + +config AS_IS_LLVM + def_bool $(success,test "$(as-name)" = LLVM) + +config AS_VERSION + int + # Use clang version if this is the integrated assembler + default CLANG_VERSION if AS_IS_LLVM + default $(as-version) + config CC_CAN_LINK bool default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m64-flag)) if 64BIT diff --git a/init/init_task.c b/init/init_task.c index afaea5d7cb8a..b27ceec394d7 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -193,6 +193,9 @@ struct task_struct init_task #ifdef CONFIG_SECURITY .security = NULL, #endif +#ifdef CONFIG_SECCOMP_FILTER + .seccomp = { .filter_count = ATOMIC_INIT(0) }, +#endif }; EXPORT_SYMBOL(init_task); diff --git a/init/main.c b/init/main.c index ea899e9d7743..cb6c59db4188 100644 --- a/init/main.c +++ b/init/main.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -557,6 +558,7 @@ static void __init mm_init(void) */ page_ext_init_flatmem(); init_debug_pagealloc(); + kfence_alloc_pool(); report_meminit(); mem_init(); /* page_owner must be initialized after buddy is ready */ @@ -623,6 +625,9 @@ asmlinkage __visible void __init start_kernel(void) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg); + /* Architectural and non-timekeeping rng init, before allocator init */ + random_init_early(command_line); + /* * These use large bootmem allocations and must precede * kmem_cache_init() @@ -687,14 +692,11 @@ asmlinkage __visible void __init start_kernel(void) timekeeping_init(); time_init(); - /* - * For best initial stack canary entropy, prepare it after: - * - setup_arch() for any UEFI RNG entropy and boot cmdline access - * - timekeeping_init() for ktime entropy used in random_init() - * - time_init() for making random_get_entropy() work on some platforms - * - random_init() to initialize the RNG from from early entropy sources - */ - random_init(command_line); + /* This must be after timekeeping is initialized */ + random_init(); + + /* These make use of the fully initialized rng */ + kfence_init(); boot_init_stack_canary(); perf_event_init(); diff --git a/kernel/exit.c b/kernel/exit.c index f8c860063100..d5d584cd79ce 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -274,6 +274,7 @@ repeat: } write_unlock_irq(&tasklist_lock); + seccomp_filter_release(p); release_thread(p); put_task_struct_rcu_user(p); diff --git a/kernel/fork.c b/kernel/fork.c index 68b18c603925..bb45f6817d52 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -474,7 +474,6 @@ void free_task(struct task_struct *tsk) #endif rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); - put_seccomp_filter(tsk); arch_release_task_struct(tsk); if (tsk->flags & PF_KTHREAD) free_kthread_struct(tsk); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index f1bff3f17583..455330c8b9ba 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -13,6 +13,7 @@ * Mode 2 allows user-defined system call filters in the form * of Berkeley Packet Filters/Linux Socket Filters. */ +#define pr_fmt(fmt) "seccomp: " fmt #include #include @@ -44,6 +45,7 @@ #include #include #include +#include /* * When SECCOMP_IOCTL_NOTIF_ID_VALID was first introduced, it had the @@ -86,6 +88,7 @@ struct seccomp_knotif { /* The return values, only valid when in SECCOMP_NOTIFY_REPLIED */ int error; long val; + u32 flags; /* Signals when this has entered SECCOMP_NOTIFY_REPLIED */ struct completion ready; @@ -104,27 +107,68 @@ struct seccomp_knotif { * filter->notify_lock. * @next_id: The id of the next request. * @notifications: A list of struct seccomp_knotif elements. - * @wqh: A wait queue for poll. */ struct notification { struct semaphore request; u64 next_id; struct list_head notifications; - wait_queue_head_t wqh; }; +#ifdef SECCOMP_ARCH_NATIVE +/** + * struct action_cache - per-filter cache of seccomp actions per + * arch/syscall pair + * + * @allow_native: A bitmap where each bit represents whether the + * filter will always allow the syscall, for the + * native architecture. + * @allow_compat: A bitmap where each bit represents whether the + * filter will always allow the syscall, for the + * compat architecture. + */ +struct action_cache { + DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR); +#ifdef SECCOMP_ARCH_COMPAT + DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR); +#endif +}; +#else +struct action_cache { }; + +static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter, + const struct seccomp_data *sd) +{ + return false; +} + +static inline void seccomp_cache_prepare(struct seccomp_filter *sfilter) +{ +} +#endif /* SECCOMP_ARCH_NATIVE */ + /** * struct seccomp_filter - container for seccomp BPF programs * - * @usage: reference count to manage the object lifetime. - * get/put helpers should be used when accessing an instance - * outside of a lifetime-guarded section. In general, this - * is only needed for handling filters shared across tasks. + * @refs: Reference count to manage the object lifetime. + * A filter's reference count is incremented for each directly + * attached task, once for the dependent filter, and if + * requested for the user notifier. When @refs reaches zero, + * the filter can be freed. + * @users: A filter's @users count is incremented for each directly + * attached task (filter installation, fork(), thread_sync), + * and once for the dependent filter (tracked in filter->prev). + * When it reaches zero it indicates that no direct or indirect + * users of that filter exist. No new tasks can get associated with + * this filter after reaching 0. The @users count is always smaller + * or equal to @refs. Hence, reaching 0 for @users does not mean + * the filter can be freed. + * @cache: cache of arch/syscall mappings to actions * @log: true if all actions except for SECCOMP_RET_ALLOW should be logged * @prev: points to a previously installed, or inherited, filter * @prog: the BPF program to evaluate * @notif: the struct that holds all notification related information * @notify_lock: A lock for all notification-related accesses. + * @wqh: A wait queue for poll if a notifier is in use. * * seccomp_filter objects are organized in a tree linked via the @prev * pointer. For any task, it appears to be a singly-linked list starting @@ -134,15 +178,18 @@ struct notification { * how namespaces work. * * seccomp_filter objects should never be modified after being attached - * to a task_struct (other than @usage). + * to a task_struct (other than @refs). */ struct seccomp_filter { - refcount_t usage; + refcount_t refs; + refcount_t users; bool log; + struct action_cache cache; struct seccomp_filter *prev; struct bpf_prog *prog; struct notification *notif; struct mutex notify_lock; + wait_queue_head_t wqh; }; /* Limit any path through the tree to 256KB worth of instructions. */ @@ -154,6 +201,10 @@ struct seccomp_filter { */ static void populate_seccomp_data(struct seccomp_data *sd) { + /* + * Instead of using current_pt_reg(), we're already doing the work + * to safely fetch "current", so just use "task" everywhere below. + */ struct task_struct *task = current; struct pt_regs *regs = task_pt_regs(task); unsigned long args[6]; @@ -252,6 +303,52 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) return 0; } +#ifdef SECCOMP_ARCH_NATIVE +static inline bool seccomp_cache_check_allow_bitmap(const void *bitmap, + size_t bitmap_size, + int syscall_nr) +{ + if (unlikely(syscall_nr < 0 || syscall_nr >= bitmap_size)) + return false; + syscall_nr = array_index_nospec(syscall_nr, bitmap_size); + + return test_bit(syscall_nr, bitmap); +} + +/** + * seccomp_cache_check_allow - lookup seccomp cache + * @sfilter: The seccomp filter + * @sd: The seccomp data to lookup the cache with + * + * Returns true if the seccomp_data is cached and allowed. + */ +static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter, + const struct seccomp_data *sd) +{ + int syscall_nr = sd->nr; + const struct action_cache *cache = &sfilter->cache; + +#ifndef SECCOMP_ARCH_COMPAT + /* A native-only architecture doesn't need to check sd->arch. */ + return seccomp_cache_check_allow_bitmap(cache->allow_native, + SECCOMP_ARCH_NATIVE_NR, + syscall_nr); +#else + if (likely(sd->arch == SECCOMP_ARCH_NATIVE)) + return seccomp_cache_check_allow_bitmap(cache->allow_native, + SECCOMP_ARCH_NATIVE_NR, + syscall_nr); + if (likely(sd->arch == SECCOMP_ARCH_COMPAT)) + return seccomp_cache_check_allow_bitmap(cache->allow_compat, + SECCOMP_ARCH_COMPAT_NR, + syscall_nr); +#endif /* SECCOMP_ARCH_COMPAT */ + + WARN_ON_ONCE(true); + return false; +} +#endif /* SECCOMP_ARCH_NATIVE */ + /** * seccomp_run_filters - evaluates all seccomp filters against @sd * @sd: optional seccomp data to be passed to filters @@ -274,6 +371,9 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd, if (WARN_ON(f == NULL)) return SECCOMP_RET_KILL_PROCESS; + if (seccomp_cache_check_allow(f, sd)) + return SECCOMP_RET_ALLOW; + /* * All filters in the list are evaluated and the lowest BPF return * value always takes priority (ignoring the DATA). @@ -378,6 +478,59 @@ static inline pid_t seccomp_can_sync_threads(void) return 0; } +static inline void seccomp_filter_free(struct seccomp_filter *filter) +{ + if (filter) { + bpf_prog_destroy(filter->prog); + kfree(filter); + } +} + +static void __seccomp_filter_orphan(struct seccomp_filter *orig) +{ + while (orig && refcount_dec_and_test(&orig->users)) { + if (waitqueue_active(&orig->wqh)) + wake_up_poll(&orig->wqh, EPOLLHUP); + orig = orig->prev; + } +} + +static void __put_seccomp_filter(struct seccomp_filter *orig) +{ + /* Clean up single-reference branches iteratively. */ + while (orig && refcount_dec_and_test(&orig->refs)) { + struct seccomp_filter *freeme = orig; + orig = orig->prev; + seccomp_filter_free(freeme); + } +} + +static void __seccomp_filter_release(struct seccomp_filter *orig) +{ + /* Notify about any unused filters in the task's former filter tree. */ + __seccomp_filter_orphan(orig); + /* Finally drop all references to the task's former tree. */ + __put_seccomp_filter(orig); +} + +/** + * seccomp_filter_release - Detach the task from its filter tree, + * drop its reference count, and notify + * about unused filters + * + * This function should only be called when the task is exiting as + * it detaches it from its filter tree. As such, READ_ONCE() and + * barriers are not needed here, as would normally be needed. + */ +void seccomp_filter_release(struct task_struct *tsk) +{ + struct seccomp_filter *orig = tsk->seccomp.filter; + + /* Detach task from its filter tree. */ + tsk->seccomp.filter = NULL; + __seccomp_filter_release(orig); +} + /** * seccomp_sync_threads: sets all threads to use current's filter * @@ -402,14 +555,19 @@ static inline void seccomp_sync_threads(unsigned long flags) /* Get a task reference for the new leaf node. */ get_seccomp_filter(caller); + /* * Drop the task reference to the shared ancestor since * current's path will hold a reference. (This also * allows a put before the assignment.) */ - put_seccomp_filter(thread); + __seccomp_filter_release(thread->seccomp.filter); + + /* Make our new filter tree visible. */ smp_store_release(&thread->seccomp.filter, caller->seccomp.filter); + atomic_set(&thread->seccomp.filter_count, + atomic_read(&caller->seccomp.filter_count)); /* * Don't let an unprivileged task work around @@ -442,7 +600,12 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) { struct seccomp_filter *sfilter; int ret; - const bool save_orig = IS_ENABLED(CONFIG_CHECKPOINT_RESTORE); + const bool save_orig = +#if defined(CONFIG_CHECKPOINT_RESTORE) || defined(SECCOMP_ARCH_NATIVE) + true; +#else + false; +#endif if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) return ERR_PTR(-EINVAL); @@ -472,7 +635,9 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) return ERR_PTR(ret); } - refcount_set(&sfilter->usage, 1); + refcount_set(&sfilter->refs, 1); + refcount_set(&sfilter->users, 1); + init_waitqueue_head(&sfilter->wqh); return sfilter; } @@ -505,6 +670,148 @@ out: return filter; } +#ifdef SECCOMP_ARCH_NATIVE +/** + * seccomp_is_const_allow - check if filter is constant allow with given data + * @fprog: The BPF programs + * @sd: The seccomp data to check against, only syscall number and arch + * number are considered constant. + */ +static bool seccomp_is_const_allow(struct sock_fprog_kern *fprog, + struct seccomp_data *sd) +{ + unsigned int reg_value = 0; + unsigned int pc; + bool op_res; + + if (WARN_ON_ONCE(!fprog)) + return false; + + for (pc = 0; pc < fprog->len; pc++) { + struct sock_filter *insn = &fprog->filter[pc]; + u16 code = insn->code; + u32 k = insn->k; + + switch (code) { + case BPF_LD | BPF_W | BPF_ABS: + switch (k) { + case offsetof(struct seccomp_data, nr): + reg_value = sd->nr; + break; + case offsetof(struct seccomp_data, arch): + reg_value = sd->arch; + break; + default: + /* can't optimize (non-constant value load) */ + return false; + } + break; + case BPF_RET | BPF_K: + /* reached return with constant values only, check allow */ + return k == SECCOMP_RET_ALLOW; + case BPF_JMP | BPF_JA: + pc += insn->k; + break; + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JSET | BPF_K: + switch (BPF_OP(code)) { + case BPF_JEQ: + op_res = reg_value == k; + break; + case BPF_JGE: + op_res = reg_value >= k; + break; + case BPF_JGT: + op_res = reg_value > k; + break; + case BPF_JSET: + op_res = !!(reg_value & k); + break; + default: + /* can't optimize (unknown jump) */ + return false; + } + + pc += op_res ? insn->jt : insn->jf; + break; + case BPF_ALU | BPF_AND | BPF_K: + reg_value &= k; + break; + default: + /* can't optimize (unknown insn) */ + return false; + } + } + + /* ran off the end of the filter?! */ + WARN_ON(1); + return false; +} + +static void seccomp_cache_prepare_bitmap(struct seccomp_filter *sfilter, + void *bitmap, const void *bitmap_prev, + size_t bitmap_size, int arch) +{ + struct sock_fprog_kern *fprog = sfilter->prog->orig_prog; + struct seccomp_data sd; + int nr; + + if (bitmap_prev) { + /* The new filter must be as restrictive as the last. */ + bitmap_copy(bitmap, bitmap_prev, bitmap_size); + } else { + /* Before any filters, all syscalls are always allowed. */ + bitmap_fill(bitmap, bitmap_size); + } + + for (nr = 0; nr < bitmap_size; nr++) { + /* No bitmap change: not a cacheable action. */ + if (!test_bit(nr, bitmap)) + continue; + + sd.nr = nr; + sd.arch = arch; + + /* No bitmap change: continue to always allow. */ + if (seccomp_is_const_allow(fprog, &sd)) + continue; + + /* + * Not a cacheable action: always run filters. + * atomic clear_bit() not needed, filter not visible yet. + */ + __clear_bit(nr, bitmap); + } +} + +/** + * seccomp_cache_prepare - emulate the filter to find cachable syscalls + * @sfilter: The seccomp filter + * + * Returns 0 if successful or -errno if error occurred. + */ +static void seccomp_cache_prepare(struct seccomp_filter *sfilter) +{ + struct action_cache *cache = &sfilter->cache; + const struct action_cache *cache_prev = + sfilter->prev ? &sfilter->prev->cache : NULL; + + seccomp_cache_prepare_bitmap(sfilter, cache->allow_native, + cache_prev ? cache_prev->allow_native : NULL, + SECCOMP_ARCH_NATIVE_NR, + SECCOMP_ARCH_NATIVE); + +#ifdef SECCOMP_ARCH_COMPAT + seccomp_cache_prepare_bitmap(sfilter, cache->allow_compat, + cache_prev ? cache_prev->allow_compat : NULL, + SECCOMP_ARCH_COMPAT_NR, + SECCOMP_ARCH_COMPAT); +#endif /* SECCOMP_ARCH_COMPAT */ +} +#endif /* SECCOMP_ARCH_NATIVE */ + /** * seccomp_attach_filter: validate and attach filter * @flags: flags to change filter behavior @@ -550,7 +857,9 @@ static long seccomp_attach_filter(unsigned int flags, * task reference. */ filter->prev = current->seccomp.filter; + seccomp_cache_prepare(filter); current->seccomp.filter = filter; + atomic_inc(¤t->seccomp.filter_count); /* Now that the new filter is in place, synchronize to all threads. */ if (flags & SECCOMP_FILTER_FLAG_TSYNC) @@ -561,7 +870,7 @@ static long seccomp_attach_filter(unsigned int flags, static void __get_seccomp_filter(struct seccomp_filter *filter) { - refcount_inc(&filter->usage); + refcount_inc(&filter->refs); } /* get_seccomp_filter - increments the reference count of the filter on @tsk */ @@ -571,30 +880,7 @@ void get_seccomp_filter(struct task_struct *tsk) if (!orig) return; __get_seccomp_filter(orig); -} - -static inline void seccomp_filter_free(struct seccomp_filter *filter) -{ - if (filter) { - bpf_prog_destroy(filter->prog); - kfree(filter); - } -} - -static void __put_seccomp_filter(struct seccomp_filter *orig) -{ - /* Clean up single-reference branches iteratively. */ - while (orig && refcount_dec_and_test(&orig->usage)) { - struct seccomp_filter *freeme = orig; - orig = orig->prev; - seccomp_filter_free(freeme); - } -} - -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */ -void put_seccomp_filter(struct task_struct *tsk) -{ - __put_seccomp_filter(tsk->seccomp.filter); + refcount_inc(&orig->users); } static void seccomp_init_siginfo(kernel_siginfo_t *info, int syscall, int reason) @@ -691,20 +977,20 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action, */ static const int mode1_syscalls[] = { __NR_seccomp_read, __NR_seccomp_write, __NR_seccomp_exit, __NR_seccomp_sigreturn, - 0, /* null terminated */ + -1, /* negative terminated */ }; static void __secure_computing_strict(int this_syscall) { - const int *syscall_whitelist = mode1_syscalls; + const int *allowed_syscalls = mode1_syscalls; #ifdef CONFIG_COMPAT if (in_compat_syscall()) - syscall_whitelist = get_compat_mode1_syscalls(); + allowed_syscalls = get_compat_mode1_syscalls(); #endif do { - if (*syscall_whitelist == this_syscall) + if (*allowed_syscalls == this_syscall) return; - } while (*++syscall_whitelist); + } while (*++allowed_syscalls != -1); #ifdef SECCOMP_DEBUG dump_stack(); @@ -743,11 +1029,12 @@ static u64 seccomp_next_notify_id(struct seccomp_filter *filter) return filter->notif->next_id++; } -static void seccomp_do_user_notification(int this_syscall, - struct seccomp_filter *match, - const struct seccomp_data *sd) +static int seccomp_do_user_notification(int this_syscall, + struct seccomp_filter *match, + const struct seccomp_data *sd) { int err; + u32 flags = 0; long ret = 0; struct seccomp_knotif n = {}; @@ -764,7 +1051,7 @@ static void seccomp_do_user_notification(int this_syscall, list_add(&n.list, &match->notif->notifications); up(&match->notif->request); - wake_up_poll(&match->notif->wqh, EPOLLIN | EPOLLRDNORM); + wake_up_poll(&match->wqh, EPOLLIN | EPOLLRDNORM); mutex_unlock(&match->notify_lock); /* @@ -775,6 +1062,7 @@ static void seccomp_do_user_notification(int this_syscall, if (err == 0) { ret = n.val; err = n.error; + flags = n.flags; } /* @@ -791,8 +1079,14 @@ static void seccomp_do_user_notification(int this_syscall, list_del(&n.list); out: mutex_unlock(&match->notify_lock); - syscall_set_return_value(current, task_pt_regs(current), + + /* Userspace requests to continue the syscall. */ + if (flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE) + return 0; + + syscall_set_return_value(current, current_pt_regs(), err, ret); + return -1; } static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, @@ -823,13 +1117,13 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, /* Set low-order bits as an errno, capped at MAX_ERRNO. */ if (data > MAX_ERRNO) data = MAX_ERRNO; - syscall_set_return_value(current, task_pt_regs(current), + syscall_set_return_value(current, current_pt_regs(), -data, 0); goto skip; case SECCOMP_RET_TRAP: /* Show the handler the original registers. */ - syscall_rollback(current, task_pt_regs(current)); + syscall_rollback(current, current_pt_regs()); /* Let the filter pass back 16 bits of data. */ seccomp_send_sigsys(this_syscall, data); goto skip; @@ -842,7 +1136,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, /* ENOSYS these calls if there is no tracer attached. */ if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { syscall_set_return_value(current, - task_pt_regs(current), + current_pt_regs(), -ENOSYS, 0); goto skip; } @@ -862,7 +1156,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, if (fatal_signal_pending(current)) goto skip; /* Check if the tracer forced the syscall to be skipped. */ - this_syscall = syscall_get_nr(current, task_pt_regs(current)); + this_syscall = syscall_get_nr(current, current_pt_regs()); if (this_syscall < 0) goto skip; @@ -878,8 +1172,10 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, return 0; case SECCOMP_RET_USER_NOTIF: - seccomp_do_user_notification(this_syscall, match, sd); - goto skip; + if (seccomp_do_user_notification(this_syscall, match, sd)) + goto skip; + + return 0; case SECCOMP_RET_LOG: seccomp_log(this_syscall, 0, action, true); @@ -899,20 +1195,20 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, current->seccomp.mode = SECCOMP_MODE_DEAD; seccomp_log(this_syscall, SIGSYS, action, true); /* Dump core only if this is the last remaining thread. */ - if (action == SECCOMP_RET_KILL_PROCESS || + if (action != SECCOMP_RET_KILL_THREAD || get_nr_threads(current) == 1) { kernel_siginfo_t info; /* Show the original registers in the dump. */ - syscall_rollback(current, task_pt_regs(current)); + syscall_rollback(current, current_pt_regs()); /* Trigger a manual coredump since do_exit skips it. */ seccomp_init_siginfo(&info, this_syscall, data); do_coredump(&info); } - if (action == SECCOMP_RET_KILL_PROCESS) - do_group_exit(SIGSYS); - else + if (action == SECCOMP_RET_KILL_THREAD) do_exit(SIGSYS); + else + do_group_exit(SIGSYS); } unreachable(); @@ -941,7 +1237,7 @@ int __secure_computing(const struct seccomp_data *sd) return 0; this_syscall = sd ? sd->nr : - syscall_get_nr(current, task_pt_regs(current)); + syscall_get_nr(current, current_pt_regs()); switch (mode) { case SECCOMP_MODE_STRICT: @@ -995,13 +1291,18 @@ out: } #ifdef CONFIG_SECCOMP_FILTER -static int seccomp_notify_release(struct inode *inode, struct file *file) +static void seccomp_notify_free(struct seccomp_filter *filter) +{ + kfree(filter->notif); + filter->notif = NULL; +} + +static void seccomp_notify_detach(struct seccomp_filter *filter) { - struct seccomp_filter *filter = file->private_data; struct seccomp_knotif *knotif; if (!filter) - return 0; + return; mutex_lock(&filter->notify_lock); @@ -1020,13 +1321,36 @@ static int seccomp_notify_release(struct inode *inode, struct file *file) complete(&knotif->ready); } - kfree(filter->notif); - filter->notif = NULL; + seccomp_notify_free(filter); mutex_unlock(&filter->notify_lock); +} + +static int seccomp_notify_release(struct inode *inode, struct file *file) +{ + struct seccomp_filter *filter = file->private_data; + + seccomp_notify_detach(filter); __put_seccomp_filter(filter); return 0; } +/* must be called with notif_lock held */ +static inline struct seccomp_knotif * +find_notification(struct seccomp_filter *filter, u64 id) +{ + struct seccomp_knotif *cur; + + lockdep_assert_held(&filter->notify_lock); + + list_for_each_entry(cur, &filter->notif->notifications, list) { + if (cur->id == id) + return cur; + } + + return NULL; +} + + static long seccomp_notify_recv(struct seccomp_filter *filter, void __user *buf) { @@ -1070,7 +1394,7 @@ static long seccomp_notify_recv(struct seccomp_filter *filter, unotif.data = *(knotif->data); knotif->state = SECCOMP_NOTIFY_SENT; - wake_up_poll(&filter->notif->wqh, EPOLLOUT | EPOLLWRNORM); + wake_up_poll(&filter->wqh, EPOLLOUT | EPOLLWRNORM); ret = 0; out: mutex_unlock(&filter->notify_lock); @@ -1084,15 +1408,8 @@ out: * may have died when we released the lock, so we need to make * sure it's still around. */ - knotif = NULL; mutex_lock(&filter->notify_lock); - list_for_each_entry(cur, &filter->notif->notifications, list) { - if (cur->id == unotif.id) { - knotif = cur; - break; - } - } - + knotif = find_notification(filter, unotif.id); if (knotif) { knotif->state = SECCOMP_NOTIFY_INIT; up(&filter->notif->request); @@ -1107,26 +1424,24 @@ static long seccomp_notify_send(struct seccomp_filter *filter, void __user *buf) { struct seccomp_notif_resp resp = {}; - struct seccomp_knotif *knotif = NULL, *cur; + struct seccomp_knotif *knotif; long ret; if (copy_from_user(&resp, buf, sizeof(resp))) return -EFAULT; - if (resp.flags) + if (resp.flags & ~SECCOMP_USER_NOTIF_FLAG_CONTINUE) + return -EINVAL; + + if ((resp.flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE) && + (resp.error || resp.val)) return -EINVAL; ret = mutex_lock_interruptible(&filter->notify_lock); if (ret < 0) return ret; - list_for_each_entry(cur, &filter->notif->notifications, list) { - if (cur->id == resp.id) { - knotif = cur; - break; - } - } - + knotif = find_notification(filter, resp.id); if (!knotif) { ret = -ENOENT; goto out; @@ -1142,6 +1457,7 @@ static long seccomp_notify_send(struct seccomp_filter *filter, knotif->state = SECCOMP_NOTIFY_REPLIED; knotif->error = resp.error; knotif->val = resp.val; + knotif->flags = resp.flags; complete(&knotif->ready); out: mutex_unlock(&filter->notify_lock); @@ -1151,7 +1467,7 @@ out: static long seccomp_notify_id_valid(struct seccomp_filter *filter, void __user *buf) { - struct seccomp_knotif *knotif = NULL; + struct seccomp_knotif *knotif; u64 id; long ret; @@ -1162,16 +1478,12 @@ static long seccomp_notify_id_valid(struct seccomp_filter *filter, if (ret < 0) return ret; - ret = -ENOENT; - list_for_each_entry(knotif, &filter->notif->notifications, list) { - if (knotif->id == id) { - if (knotif->state == SECCOMP_NOTIFY_SENT) - ret = 0; - goto out; - } - } + knotif = find_notification(filter, id); + if (knotif && knotif->state == SECCOMP_NOTIFY_SENT) + ret = 0; + else + ret = -ENOENT; -out: mutex_unlock(&filter->notify_lock); return ret; } @@ -1202,7 +1514,7 @@ static __poll_t seccomp_notify_poll(struct file *file, __poll_t ret = 0; struct seccomp_knotif *cur; - poll_wait(file, &filter->notif->wqh, poll_tab); + poll_wait(file, &filter->wqh, poll_tab); if (mutex_lock_interruptible(&filter->notify_lock) < 0) return EPOLLERR; @@ -1218,6 +1530,9 @@ static __poll_t seccomp_notify_poll(struct file *file, mutex_unlock(&filter->notify_lock); + if (refcount_read(&filter->users) == 0) + ret |= EPOLLHUP; + return ret; } @@ -1240,7 +1555,6 @@ static struct file *init_listener(struct seccomp_filter *filter) sema_init(&filter->notif->request, 0); filter->notif->next_id = get_random_u64(); INIT_LIST_HEAD(&filter->notif->notifications); - init_waitqueue_head(&filter->notif->wqh); ret = anon_inode_getfile("seccomp notify", &seccomp_notify_ops, filter, O_RDWR); @@ -1252,7 +1566,7 @@ static struct file *init_listener(struct seccomp_filter *filter) out_notif: if (IS_ERR(ret)) - kfree(filter->notif); + seccomp_notify_free(filter); out: return ret; } @@ -1373,6 +1687,7 @@ out_put_fd: listener_f->private_data = NULL; fput(listener_f); put_unused_fd(listener); + seccomp_notify_detach(prepared); } else { fd_install(listener, listener_f); ret = listener; @@ -1720,7 +2035,7 @@ static bool seccomp_actions_logged_from_names(u32 *actions_logged, char *names) return true; } -static int read_actions_logged(struct ctl_table *ro_table, void *buffer, +static int read_actions_logged(struct ctl_table *ro_table, void __user *buffer, size_t *lenp, loff_t *ppos) { char names[sizeof(seccomp_actions_avail)]; @@ -1738,7 +2053,7 @@ static int read_actions_logged(struct ctl_table *ro_table, void *buffer, return proc_dostring(&table, 0, buffer, lenp, ppos); } -static int write_actions_logged(struct ctl_table *ro_table, void *buffer, +static int write_actions_logged(struct ctl_table *ro_table, void __user *buffer, size_t *lenp, loff_t *ppos, u32 *actions_logged) { char names[sizeof(seccomp_actions_avail)]; @@ -1846,7 +2161,7 @@ static int __init seccomp_sysctl_init(void) hdr = register_sysctl_paths(seccomp_sysctl_path, seccomp_sysctl_table); if (!hdr) - pr_warn("seccomp: sysctl registration failed\n"); + pr_warn("sysctl registration failed\n"); else kmemleak_not_leak(hdr); diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 3f30b6c2d659..d09dfa4226b8 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o obj-$(CONFIG_KPROBE_EVENTS) += trace_kprobe.o +obj-$(CONFIG_TRACEPOINTS) += error_report-traces.o obj-$(CONFIG_TRACEPOINTS) += power-traces.o ifeq ($(CONFIG_PM),y) obj-$(CONFIG_TRACEPOINTS) += rpm-traces.o diff --git a/kernel/trace/error_report-traces.c b/kernel/trace/error_report-traces.c new file mode 100644 index 000000000000..632c8c7ff079 --- /dev/null +++ b/kernel/trace/error_report-traces.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Error reporting trace points. + * + * Copyright (C) 2021, Google LLC. + */ + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL_GPL(error_report_end); + diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index b74832919af2..f47cbf5660b1 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -782,6 +782,7 @@ config DEBUG_STACKOVERFLOW If in doubt, say "N". source "lib/Kconfig.kasan" +source "lib/Kconfig.kfence" endmenu # "Memory Debugging" diff --git a/lib/Kconfig.kfence b/lib/Kconfig.kfence new file mode 100644 index 000000000000..e641add33947 --- /dev/null +++ b/lib/Kconfig.kfence @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config HAVE_ARCH_KFENCE + bool + +menuconfig KFENCE + bool "KFENCE: low-overhead sampling-based memory safety error detector" + depends on HAVE_ARCH_KFENCE && (SLAB || SLUB) + select STACKTRACE + select IRQ_WORK + help + KFENCE is a low-overhead sampling-based detector of heap out-of-bounds + access, use-after-free, and invalid-free errors. KFENCE is designed + to have negligible cost to permit enabling it in production + environments. + + See for more details. + + Note that, KFENCE is not a substitute for explicit testing with tools + such as KASAN. KFENCE can detect a subset of bugs that KASAN can + detect, albeit at very different performance profiles. If you can + afford to use KASAN, continue using KASAN, for example in test + environments. If your kernel targets production use, and cannot + enable KASAN due to its cost, consider using KFENCE. + +if KFENCE + +config KFENCE_STATIC_KEYS + bool "Use static keys to set up allocations" + default y + depends on JUMP_LABEL # To ensure performance, require jump labels + help + Use static keys (static branches) to set up KFENCE allocations. Using + static keys is normally recommended, because it avoids a dynamic + branch in the allocator's fast path. However, with very low sample + intervals, or on systems that do not support jump labels, a dynamic + branch may still be an acceptable performance trade-off. + +config KFENCE_SAMPLE_INTERVAL + int "Default sample interval in milliseconds" + default 100 + help + The KFENCE sample interval determines the frequency with which heap + allocations will be guarded by KFENCE. May be overridden via boot + parameter "kfence.sample_interval". + + Set this to 0 to disable KFENCE by default, in which case only + setting "kfence.sample_interval" to a non-zero value enables KFENCE. + +config KFENCE_NUM_OBJECTS + int "Number of guarded objects available" + range 1 65535 + default 255 + help + The number of guarded objects available. For each KFENCE object, 2 + pages are required; with one containing the object and two adjacent + ones used as guard pages. + +config KFENCE_STRESS_TEST_FAULTS + int "Stress testing of fault handling and error reporting" if EXPERT + default 0 + help + The inverse probability with which to randomly protect KFENCE object + pages, resulting in spurious use-after-frees. The main purpose of + this option is to stress test KFENCE with concurrent error reports + and allocations/frees. A value of 0 disables stress testing logic. + + Only for KFENCE testing; set to 0 if you are not a KFENCE developer. + +config KFENCE_KUNIT_TEST + tristate "KFENCE integration test suite" if !KUNIT_ALL_TESTS + default KUNIT_ALL_TESTS + depends on TRACEPOINTS && KUNIT + help + Test suite for KFENCE, testing various error detection scenarios with + various allocation types, and checking that reports are correctly + output to console. + + Say Y here if you want the test to be built into the kernel and run + during boot; say M if you want the test to build as a module; say N + if you are unsure. + +endif # KFENCE diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 51a98f7ee79e..1ee9d95898b5 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -16,8 +16,7 @@ #include "../tools/testing/selftests/kselftest_module.h" -static unsigned total_tests __initdata; -static unsigned failed_tests __initdata; +KSTM_MODULE_GLOBALS(); static char pbl_buffer[PAGE_SIZE] __initdata; diff --git a/lib/test_printf.c b/lib/test_printf.c index d4b711b53942..0a3153f162c7 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -28,11 +28,13 @@ #define PAD_SIZE 16 #define FILL_CHAR '$' -static unsigned total_tests __initdata; -static unsigned failed_tests __initdata; +KSTM_MODULE_GLOBALS(); + static char *test_buffer __initdata; static char *alloced_buffer __initdata; +extern bool no_hash_pointers; + static int __printf(4, 0) __init do_test(int bufsize, const char *expect, int elen, const char *fmt, va_list ap) @@ -299,6 +301,12 @@ plain(void) { int err; + if (no_hash_pointers) { + pr_warn("skipping plain 'p' tests"); + skipped_tests += 2; + return; + } + err = plain_hash(); if (err) { pr_warn("plain 'p' does not appear to be hashed\n"); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d377456b8c39..f3d84136199b 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -2057,6 +2057,32 @@ static char *kobject_string(char *buf, char *end, void *ptr, return error_string(buf, end, "(%pO?)", spec); } +/* Disable pointer hashing if requested */ +bool no_hash_pointers __ro_after_init; +EXPORT_SYMBOL_GPL(no_hash_pointers); + +static int __init no_hash_pointers_enable(char *str) +{ + no_hash_pointers = true; + + pr_warn("**********************************************************\n"); + pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_warn("** **\n"); + pr_warn("** This system shows unhashed kernel memory addresses **\n"); + pr_warn("** via the console, logs, and other interfaces. This **\n"); + pr_warn("** might reduce the security of your system. **\n"); + pr_warn("** **\n"); + pr_warn("** If you see this message and you are not debugging **\n"); + pr_warn("** the kernel, report this immediately to your system **\n"); + pr_warn("** administrator! **\n"); + pr_warn("** **\n"); + pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); + pr_warn("**********************************************************\n"); + + return 0; +} +early_param("no_hash_pointers", no_hash_pointers_enable); + /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -2259,8 +2285,14 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, if (IS_ENABLED(CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS)) return pointer_string(buf, end, ptr, spec); - /* default is to _not_ leak addresses, hash before printing */ - return ptr_to_id(buf, end, ptr, spec); + /* + * default is to _not_ leak addresses, so hash before printing, + * unless no_hash_pointers is specified on the command line. + */ + if (unlikely(no_hash_pointers)) + return pointer_string(buf, end, ptr, spec); + else + return ptr_to_id(buf, end, ptr, spec); } /* diff --git a/mm/Makefile b/mm/Makefile index d433568ee189..7d852be8167b 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_PAGE_POISONING) += page_poison.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_KASAN) += kasan/ +obj-$(CONFIG_KFENCE) += kfence/ obj-$(CONFIG_FAILSLAB) += failslab.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o obj-$(CONFIG_MEMTEST) += memtest.o diff --git a/mm/kasan/common.c b/mm/kasan/common.c index ac9f624158b9..b71b16da066e 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,10 @@ void kasan_poison_shadow(const void *address, size_t size, u8 value) */ address = reset_tag(address); + /* Skip KFENCE memory if called explicitly outside of sl*b. */ + if (is_kfence_address(address)) + return; + shadow_start = kasan_mem_to_shadow(address); shadow_end = kasan_mem_to_shadow(address + size); @@ -157,6 +162,14 @@ void kasan_unpoison_shadow(const void *address, size_t size) */ address = reset_tag(address); + /* + * Skip KFENCE memory if called explicitly outside of sl*b. Also note + * that calls to ksize(), where size is not a multiple of machine-word + * size, would otherwise poison the invalid portion of the word. + */ + if (is_kfence_address(address)) + return; + kasan_poison_shadow(address, size, tag); if (size & KASAN_SHADOW_MASK) { @@ -445,6 +458,9 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object, tagged_object = object; object = reset_tag(object); + if (is_kfence_address(object)) + return false; + if (unlikely(nearest_obj(cache, virt_to_head_page(object), object) != object)) { kasan_report_invalid_free(tagged_object, ip); @@ -493,6 +509,9 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object, if (unlikely(object == NULL)) return NULL; + if (is_kfence_address(kasan_reset_tag(object))) + return (void *)object; + redzone_start = round_up((unsigned long)(object + size), KASAN_SHADOW_SCALE_SIZE); redzone_end = round_up((unsigned long)object + cache->object_size, diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 4d87df96acc1..783301f294de 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -87,8 +88,9 @@ static void start_report(unsigned long *flags) pr_err("==================================================================\n"); } -static void end_report(unsigned long *flags) +static void end_report(unsigned long *flags, unsigned long addr) { + trace_error_report_end(ERROR_DETECTOR_KASAN, addr); pr_err("==================================================================\n"); add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irqrestore(&report_lock, *flags); @@ -468,7 +470,7 @@ void kasan_report_invalid_free(void *object, unsigned long ip) print_address_description(object, tag); pr_err("\n"); print_shadow_for_address(object); - end_report(&flags); + end_report(&flags, (unsigned long)object); } void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip) @@ -510,5 +512,5 @@ void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned lon dump_stack(); } - end_report(&flags); + end_report(&flags, addr); } diff --git a/mm/kfence/Makefile b/mm/kfence/Makefile new file mode 100644 index 000000000000..cb2bcf773083 --- /dev/null +++ b/mm/kfence/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_KFENCE) := core.o report.o + +CFLAGS_kfence_test.o := -fno-omit-frame-pointer -fno-optimize-sibling-calls +obj-$(CONFIG_KFENCE_KUNIT_TEST) += kfence_test.o diff --git a/mm/kfence/core.c b/mm/kfence/core.c new file mode 100644 index 000000000000..566a82c3cb20 --- /dev/null +++ b/mm/kfence/core.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KFENCE guarded object allocator and fault handling. + * + * Copyright (C) 2020, Google LLC. + */ + +#define pr_fmt(fmt) "kfence: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kfence.h" + +/* + * Android 4.19 kernel does not support KCSAN, so we have to disable data race + * annotations. + */ +#ifndef data_race +#define data_race(x) (x) +#endif + +/* Disables KFENCE on the first warning assuming an irrecoverable error. */ +#define KFENCE_WARN_ON(cond) \ + ({ \ + const bool __cond = WARN_ON(cond); \ + if (unlikely(__cond)) \ + WRITE_ONCE(kfence_enabled, false); \ + __cond; \ + }) + +/* === Data ================================================================= */ + +static bool kfence_enabled __read_mostly; + +static unsigned long kfence_sample_interval __read_mostly = CONFIG_KFENCE_SAMPLE_INTERVAL; + +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "kfence." + +static int param_set_sample_interval(const char *val, const struct kernel_param *kp) +{ + unsigned long num; + int ret = kstrtoul(val, 0, &num); + + if (ret < 0) + return ret; + + if (!num) /* Using 0 to indicate KFENCE is disabled. */ + WRITE_ONCE(kfence_enabled, false); + else if (!READ_ONCE(kfence_enabled) && system_state != SYSTEM_BOOTING) + return -EINVAL; /* Cannot (re-)enable KFENCE on-the-fly. */ + + *((unsigned long *)kp->arg) = num; + return 0; +} + +static int param_get_sample_interval(char *buffer, const struct kernel_param *kp) +{ + if (!READ_ONCE(kfence_enabled)) + return sprintf(buffer, "0\n"); + + return param_get_ulong(buffer, kp); +} + +static const struct kernel_param_ops sample_interval_param_ops = { + .set = param_set_sample_interval, + .get = param_get_sample_interval, +}; +module_param_cb(sample_interval, &sample_interval_param_ops, &kfence_sample_interval, 0600); + +/* The pool of pages used for guard pages and objects. */ +char *__kfence_pool __ro_after_init; +EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */ + +/* + * Per-object metadata, with one-to-one mapping of object metadata to + * backing pages (in __kfence_pool). + */ +static_assert(CONFIG_KFENCE_NUM_OBJECTS > 0); +struct kfence_metadata kfence_metadata[CONFIG_KFENCE_NUM_OBJECTS]; + +/* Freelist with available objects. */ +static struct list_head kfence_freelist = LIST_HEAD_INIT(kfence_freelist); +static DEFINE_RAW_SPINLOCK(kfence_freelist_lock); /* Lock protecting freelist. */ + +#ifdef CONFIG_KFENCE_STATIC_KEYS +/* The static key to set up a KFENCE allocation. */ +DEFINE_STATIC_KEY_FALSE(kfence_allocation_key); +#endif + +/* Gates the allocation, ensuring only one succeeds in a given period. */ +atomic_t kfence_allocation_gate = ATOMIC_INIT(1); + +/* Statistics counters for debugfs. */ +enum kfence_counter_id { + KFENCE_COUNTER_ALLOCATED, + KFENCE_COUNTER_ALLOCS, + KFENCE_COUNTER_FREES, + KFENCE_COUNTER_ZOMBIES, + KFENCE_COUNTER_BUGS, + KFENCE_COUNTER_COUNT, +}; +static atomic_long_t counters[KFENCE_COUNTER_COUNT]; +static const char *const counter_names[] = { + [KFENCE_COUNTER_ALLOCATED] = "currently allocated", + [KFENCE_COUNTER_ALLOCS] = "total allocations", + [KFENCE_COUNTER_FREES] = "total frees", + [KFENCE_COUNTER_ZOMBIES] = "zombie allocations", + [KFENCE_COUNTER_BUGS] = "total bugs", +}; +static_assert(ARRAY_SIZE(counter_names) == KFENCE_COUNTER_COUNT); + +/* === Internals ============================================================ */ + +static bool kfence_protect(unsigned long addr) +{ + return !KFENCE_WARN_ON(!kfence_protect_page(ALIGN_DOWN(addr, PAGE_SIZE), true)); +} + +static bool kfence_unprotect(unsigned long addr) +{ + return !KFENCE_WARN_ON(!kfence_protect_page(ALIGN_DOWN(addr, PAGE_SIZE), false)); +} + +static inline struct kfence_metadata *addr_to_metadata(unsigned long addr) +{ + long index; + + /* The checks do not affect performance; only called from slow-paths. */ + + if (!is_kfence_address((void *)addr)) + return NULL; + + /* + * May be an invalid index if called with an address at the edge of + * __kfence_pool, in which case we would report an "invalid access" + * error. + */ + index = (addr - (unsigned long)__kfence_pool) / (PAGE_SIZE * 2) - 1; + if (index < 0 || index >= CONFIG_KFENCE_NUM_OBJECTS) + return NULL; + + return &kfence_metadata[index]; +} + +static inline unsigned long metadata_to_pageaddr(const struct kfence_metadata *meta) +{ + unsigned long offset = (meta - kfence_metadata + 1) * PAGE_SIZE * 2; + unsigned long pageaddr = (unsigned long)&__kfence_pool[offset]; + + /* The checks do not affect performance; only called from slow-paths. */ + + /* Only call with a pointer into kfence_metadata. */ + if (KFENCE_WARN_ON(meta < kfence_metadata || + meta >= kfence_metadata + CONFIG_KFENCE_NUM_OBJECTS)) + return 0; + + /* + * This metadata object only ever maps to 1 page; verify that the stored + * address is in the expected range. + */ + if (KFENCE_WARN_ON(ALIGN_DOWN(meta->addr, PAGE_SIZE) != pageaddr)) + return 0; + + return pageaddr; +} + +/* + * Update the object's metadata state, including updating the alloc/free stacks + * depending on the state transition. + */ +static noinline void metadata_update_state(struct kfence_metadata *meta, + enum kfence_object_state next) +{ + struct kfence_track *track = + next == KFENCE_OBJECT_FREED ? &meta->free_track : &meta->alloc_track; + + lockdep_assert_held(&meta->lock); + + /* + * Skip over 1 (this) functions; noinline ensures we do not accidentally + * skip over the caller by never inlining. + */ + track->num_stack_entries = stack_trace_save(track->stack_entries, KFENCE_STACK_DEPTH, 1); + track->pid = task_pid_nr(current); + + /* + * Pairs with READ_ONCE() in + * kfence_shutdown_cache(), + * kfence_handle_page_fault(). + */ + WRITE_ONCE(meta->state, next); +} + +/* Write canary byte to @addr. */ +static inline bool set_canary_byte(u8 *addr) +{ + *addr = KFENCE_CANARY_PATTERN(addr); + return true; +} + +/* Check canary byte at @addr. */ +static inline bool check_canary_byte(u8 *addr) +{ + if (likely(*addr == KFENCE_CANARY_PATTERN(addr))) + return true; + + atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]); + kfence_report_error((unsigned long)addr, false, NULL, addr_to_metadata((unsigned long)addr), + KFENCE_ERROR_CORRUPTION); + return false; +} + +/* __always_inline this to ensure we won't do an indirect call to fn. */ +static __always_inline void for_each_canary(const struct kfence_metadata *meta, bool (*fn)(u8 *)) +{ + const unsigned long pageaddr = ALIGN_DOWN(meta->addr, PAGE_SIZE); + unsigned long addr; + + lockdep_assert_held(&meta->lock); + + /* + * We'll iterate over each canary byte per-side until fn() returns + * false. However, we'll still iterate over the canary bytes to the + * right of the object even if there was an error in the canary bytes to + * the left of the object. Specifically, if check_canary_byte() + * generates an error, showing both sides might give more clues as to + * what the error is about when displaying which bytes were corrupted. + */ + + /* Apply to left of object. */ + for (addr = pageaddr; addr < meta->addr; addr++) { + if (!fn((u8 *)addr)) + break; + } + + /* Apply to right of object. */ + for (addr = meta->addr + meta->size; addr < pageaddr + PAGE_SIZE; addr++) { + if (!fn((u8 *)addr)) + break; + } +} + +static void *kfence_guarded_alloc(struct kmem_cache *cache, size_t size, gfp_t gfp) +{ + struct kfence_metadata *meta = NULL; + unsigned long flags; + struct page *page; + void *addr; + + /* Try to obtain a free object. */ + raw_spin_lock_irqsave(&kfence_freelist_lock, flags); + if (!list_empty(&kfence_freelist)) { + meta = list_entry(kfence_freelist.next, struct kfence_metadata, list); + list_del_init(&meta->list); + } + raw_spin_unlock_irqrestore(&kfence_freelist_lock, flags); + if (!meta) + return NULL; + + if (unlikely(!raw_spin_trylock_irqsave(&meta->lock, flags))) { + /* + * This is extremely unlikely -- we are reporting on a + * use-after-free, which locked meta->lock, and the reporting + * code via printk calls kmalloc() which ends up in + * kfence_alloc() and tries to grab the same object that we're + * reporting on. While it has never been observed, lockdep does + * report that there is a possibility of deadlock. Fix it by + * using trylock and bailing out gracefully. + */ + raw_spin_lock_irqsave(&kfence_freelist_lock, flags); + /* Put the object back on the freelist. */ + list_add_tail(&meta->list, &kfence_freelist); + raw_spin_unlock_irqrestore(&kfence_freelist_lock, flags); + + return NULL; + } + + meta->addr = metadata_to_pageaddr(meta); + /* Unprotect if we're reusing this page. */ + if (meta->state == KFENCE_OBJECT_FREED) + kfence_unprotect(meta->addr); + + /* + * Note: for allocations made before RNG initialization, will always + * return zero. We still benefit from enabling KFENCE as early as + * possible, even when the RNG is not yet available, as this will allow + * KFENCE to detect bugs due to earlier allocations. The only downside + * is that the out-of-bounds accesses detected are deterministic for + * such allocations. + */ + if (prandom_u32_max(2)) { + /* Allocate on the "right" side, re-calculate address. */ + meta->addr += PAGE_SIZE - size; + meta->addr = ALIGN_DOWN(meta->addr, cache->align); + } + + addr = (void *)meta->addr; + + /* Update remaining metadata. */ + metadata_update_state(meta, KFENCE_OBJECT_ALLOCATED); + /* Pairs with READ_ONCE() in kfence_shutdown_cache(). */ + WRITE_ONCE(meta->cache, cache); + meta->size = size; + for_each_canary(meta, set_canary_byte); + + /* Set required struct page fields. */ + page = virt_to_page(meta->addr); + page->slab_cache = cache; + if (IS_ENABLED(CONFIG_SLUB)) + page->objects = 1; + if (IS_ENABLED(CONFIG_SLAB)) + page->s_mem = addr; + + raw_spin_unlock_irqrestore(&meta->lock, flags); + + /* Memory initialization. */ + + /* + * We check slab_want_init_on_alloc() ourselves, rather than letting + * SL*B do the initialization, as otherwise we might overwrite KFENCE's + * redzone. + */ + if (unlikely(slab_want_init_on_alloc(gfp, cache))) + memzero_explicit(addr, size); + if (cache->ctor) + cache->ctor(addr); + + if (CONFIG_KFENCE_STRESS_TEST_FAULTS && !prandom_u32_max(CONFIG_KFENCE_STRESS_TEST_FAULTS)) + kfence_protect(meta->addr); /* Random "faults" by protecting the object. */ + + atomic_long_inc(&counters[KFENCE_COUNTER_ALLOCATED]); + atomic_long_inc(&counters[KFENCE_COUNTER_ALLOCS]); + + return addr; +} + +static void kfence_guarded_free(void *addr, struct kfence_metadata *meta, bool zombie) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&meta->lock, flags); + + if (meta->state != KFENCE_OBJECT_ALLOCATED || meta->addr != (unsigned long)addr) { + /* Invalid or double-free, bail out. */ + atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]); + kfence_report_error((unsigned long)addr, false, NULL, meta, + KFENCE_ERROR_INVALID_FREE); + raw_spin_unlock_irqrestore(&meta->lock, flags); + return; + } + + if (CONFIG_KFENCE_STRESS_TEST_FAULTS) + kfence_unprotect((unsigned long)addr); /* To check canary bytes. */ + + /* Restore page protection if there was an OOB access. */ + if (meta->unprotected_page) { + memzero_explicit((void *)ALIGN_DOWN(meta->unprotected_page, PAGE_SIZE), PAGE_SIZE); + kfence_protect(meta->unprotected_page); + meta->unprotected_page = 0; + } + + /* Check canary bytes for memory corruption. */ + for_each_canary(meta, check_canary_byte); + + /* + * Clear memory if init-on-free is set. While we protect the page, the + * data is still there, and after a use-after-free is detected, we + * unprotect the page, so the data is still accessible. + */ + if (!zombie && unlikely(slab_want_init_on_free(meta->cache))) + memzero_explicit(addr, meta->size); + + /* Mark the object as freed. */ + metadata_update_state(meta, KFENCE_OBJECT_FREED); + + raw_spin_unlock_irqrestore(&meta->lock, flags); + + /* Protect to detect use-after-frees. */ + kfence_protect((unsigned long)addr); + + if (!zombie) { + /* Add it to the tail of the freelist for reuse. */ + raw_spin_lock_irqsave(&kfence_freelist_lock, flags); + KFENCE_WARN_ON(!list_empty(&meta->list)); + list_add_tail(&meta->list, &kfence_freelist); + raw_spin_unlock_irqrestore(&kfence_freelist_lock, flags); + + atomic_long_dec(&counters[KFENCE_COUNTER_ALLOCATED]); + atomic_long_inc(&counters[KFENCE_COUNTER_FREES]); + } else { + /* See kfence_shutdown_cache(). */ + atomic_long_inc(&counters[KFENCE_COUNTER_ZOMBIES]); + } +} + +static void rcu_guarded_free(struct rcu_head *h) +{ + struct kfence_metadata *meta = container_of(h, struct kfence_metadata, rcu_head); + + kfence_guarded_free((void *)meta->addr, meta, false); +} + +static bool __init kfence_init_pool(void) +{ + unsigned long addr = (unsigned long)__kfence_pool; + struct page *pages; + int i; + + if (!__kfence_pool) + return false; + + if (!arch_kfence_init_pool()) + goto err; + + pages = virt_to_page(addr); + + /* + * Set up object pages: they must have PG_slab set, to avoid freeing + * these as real pages. + * + * We also want to avoid inserting kfence_free() in the kfree() + * fast-path in SLUB, and therefore need to ensure kfree() correctly + * enters __slab_free() slow-path. + */ + for (i = 0; i < KFENCE_POOL_SIZE / PAGE_SIZE; i++) { + if (!i || (i % 2)) + continue; + + /* Verify we do not have a compound head page. */ + if (WARN_ON(compound_head(&pages[i]) != &pages[i])) + goto err; + + __SetPageSlab(&pages[i]); + } + + /* + * Protect the first 2 pages. The first page is mostly unnecessary, and + * merely serves as an extended guard page. However, adding one + * additional page in the beginning gives us an even number of pages, + * which simplifies the mapping of address to metadata index. + */ + for (i = 0; i < 2; i++) { + if (unlikely(!kfence_protect(addr))) + goto err; + + addr += PAGE_SIZE; + } + + for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) { + struct kfence_metadata *meta = &kfence_metadata[i]; + + /* Initialize metadata. */ + INIT_LIST_HEAD(&meta->list); + raw_spin_lock_init(&meta->lock); + meta->state = KFENCE_OBJECT_UNUSED; + meta->addr = addr; /* Initialize for validation in metadata_to_pageaddr(). */ + list_add_tail(&meta->list, &kfence_freelist); + + /* Protect the right redzone. */ + if (unlikely(!kfence_protect(addr + PAGE_SIZE))) + goto err; + + addr += 2 * PAGE_SIZE; + } + + /* + * The pool is live and will never be deallocated from this point on. + * Remove the pool object from the kmemleak object tree, as it would + * otherwise overlap with allocations returned by kfence_alloc(), which + * are registered with kmemleak through the slab post-alloc hook. + */ + kmemleak_free(__kfence_pool); + + return true; + +err: + /* + * Only release unprotected pages, and do not try to go back and change + * page attributes due to risk of failing to do so as well. If changing + * page attributes for some pages fails, it is very likely that it also + * fails for the first page, and therefore expect addr==__kfence_pool in + * most failure cases. + */ + memblock_free_late(__pa(addr), KFENCE_POOL_SIZE - (addr - (unsigned long)__kfence_pool)); + __kfence_pool = NULL; + return false; +} + +/* === DebugFS Interface ==================================================== */ + +static int stats_show(struct seq_file *seq, void *v) +{ + int i; + + seq_printf(seq, "enabled: %i\n", READ_ONCE(kfence_enabled)); + for (i = 0; i < KFENCE_COUNTER_COUNT; i++) + seq_printf(seq, "%s: %ld\n", counter_names[i], atomic_long_read(&counters[i])); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(stats); + +/* + * debugfs seq_file operations for /sys/kernel/debug/kfence/objects. + * start_object() and next_object() return the object index + 1, because NULL is used + * to stop iteration. + */ +static void *start_object(struct seq_file *seq, loff_t *pos) +{ + if (*pos < CONFIG_KFENCE_NUM_OBJECTS) + return (void *)((long)*pos + 1); + return NULL; +} + +static void stop_object(struct seq_file *seq, void *v) +{ +} + +static void *next_object(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + if (*pos < CONFIG_KFENCE_NUM_OBJECTS) + return (void *)((long)*pos + 1); + return NULL; +} + +static int show_object(struct seq_file *seq, void *v) +{ + struct kfence_metadata *meta = &kfence_metadata[(long)v - 1]; + unsigned long flags; + + raw_spin_lock_irqsave(&meta->lock, flags); + kfence_print_object(seq, meta); + raw_spin_unlock_irqrestore(&meta->lock, flags); + seq_puts(seq, "---------------------------------\n"); + + return 0; +} + +static const struct seq_operations object_seqops = { + .start = start_object, + .next = next_object, + .stop = stop_object, + .show = show_object, +}; + +static int open_objects(struct inode *inode, struct file *file) +{ + return seq_open(file, &object_seqops); +} + +static const struct file_operations objects_fops = { + .open = open_objects, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init kfence_debugfs_init(void) +{ + struct dentry *kfence_dir = debugfs_create_dir("kfence", NULL); + + debugfs_create_file("stats", 0444, kfence_dir, NULL, &stats_fops); + debugfs_create_file("objects", 0400, kfence_dir, NULL, &objects_fops); + return 0; +} + +late_initcall(kfence_debugfs_init); + +/* === Allocation Gate Timer ================================================ */ + +#ifdef CONFIG_KFENCE_STATIC_KEYS +/* Wait queue to wake up allocation-gate timer task. */ +static DECLARE_WAIT_QUEUE_HEAD(allocation_wait); + +static void wake_up_kfence_timer(struct irq_work *work) +{ + wake_up(&allocation_wait); +} +static DEFINE_IRQ_WORK(wake_up_kfence_timer_work, wake_up_kfence_timer); +#endif + +/* + * Set up delayed work, which will enable and disable the static key. We need to + * use a work queue (rather than a simple timer), since enabling and disabling a + * static key cannot be done from an interrupt. + * + * Note: Toggling a static branch currently causes IPIs, and here we'll end up + * with a total of 2 IPIs to all CPUs. If this ends up a problem in future (with + * more aggressive sampling intervals), we could get away with a variant that + * avoids IPIs, at the cost of not immediately capturing allocations if the + * instructions remain cached. + */ +static struct delayed_work kfence_timer; +static void toggle_allocation_gate(struct work_struct *work) +{ + if (!READ_ONCE(kfence_enabled)) + return; + + atomic_set(&kfence_allocation_gate, 0); +#ifdef CONFIG_KFENCE_STATIC_KEYS + /* Enable static key, and await allocation to happen. */ + static_branch_enable(&kfence_allocation_key); + + if (sysctl_hung_task_timeout_secs) { + /* + * During low activity with no allocations we might wait a + * while; let's avoid the hung task warning. + */ + wait_event_idle_timeout(allocation_wait, atomic_read(&kfence_allocation_gate), + sysctl_hung_task_timeout_secs * HZ / 2); + } else { + wait_event_idle(allocation_wait, atomic_read(&kfence_allocation_gate)); + } + + /* Disable static key and reset timer. */ + static_branch_disable(&kfence_allocation_key); +#endif + queue_delayed_work(system_unbound_wq, &kfence_timer, + msecs_to_jiffies(kfence_sample_interval)); +} +static DECLARE_DELAYED_WORK(kfence_timer, toggle_allocation_gate); + +/* === Public interface ===================================================== */ + +void __init kfence_alloc_pool(void) +{ + if (!kfence_sample_interval) + return; + + __kfence_pool = memblock_alloc(KFENCE_POOL_SIZE, PAGE_SIZE); + + if (!__kfence_pool) + pr_err("failed to allocate pool\n"); +} + +void __init kfence_init(void) +{ + /* Setting kfence_sample_interval to 0 on boot disables KFENCE. */ + if (!kfence_sample_interval) + return; + + if (!kfence_init_pool()) { + pr_err("%s failed\n", __func__); + return; + } + + WRITE_ONCE(kfence_enabled, true); + queue_delayed_work(system_unbound_wq, &kfence_timer, 0); + pr_info("initialized - using %lu bytes for %d objects at 0x%p-0x%p\n", KFENCE_POOL_SIZE, + CONFIG_KFENCE_NUM_OBJECTS, (void *)__kfence_pool, + (void *)(__kfence_pool + KFENCE_POOL_SIZE)); +} + +void kfence_shutdown_cache(struct kmem_cache *s) +{ + unsigned long flags; + struct kfence_metadata *meta; + int i; + + for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) { + bool in_use; + + meta = &kfence_metadata[i]; + + /* + * If we observe some inconsistent cache and state pair where we + * should have returned false here, cache destruction is racing + * with either kmem_cache_alloc() or kmem_cache_free(). Taking + * the lock will not help, as different critical section + * serialization will have the same outcome. + */ + if (READ_ONCE(meta->cache) != s || + READ_ONCE(meta->state) != KFENCE_OBJECT_ALLOCATED) + continue; + + raw_spin_lock_irqsave(&meta->lock, flags); + in_use = meta->cache == s && meta->state == KFENCE_OBJECT_ALLOCATED; + raw_spin_unlock_irqrestore(&meta->lock, flags); + + if (in_use) { + /* + * This cache still has allocations, and we should not + * release them back into the freelist so they can still + * safely be used and retain the kernel's default + * behaviour of keeping the allocations alive (leak the + * cache); however, they effectively become "zombie + * allocations" as the KFENCE objects are the only ones + * still in use and the owning cache is being destroyed. + * + * We mark them freed, so that any subsequent use shows + * more useful error messages that will include stack + * traces of the user of the object, the original + * allocation, and caller to shutdown_cache(). + */ + kfence_guarded_free((void *)meta->addr, meta, /*zombie=*/true); + } + } + + for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) { + meta = &kfence_metadata[i]; + + /* See above. */ + if (READ_ONCE(meta->cache) != s || READ_ONCE(meta->state) != KFENCE_OBJECT_FREED) + continue; + + raw_spin_lock_irqsave(&meta->lock, flags); + if (meta->cache == s && meta->state == KFENCE_OBJECT_FREED) + meta->cache = NULL; + raw_spin_unlock_irqrestore(&meta->lock, flags); + } +} + +void *__kfence_alloc(struct kmem_cache *s, size_t size, gfp_t flags) +{ + /* + * Perform size check before switching kfence_allocation_gate, so that + * we don't disable KFENCE without making an allocation. + */ + if (size > PAGE_SIZE) + return NULL; + + /* + * Skip allocations from non-default zones, including DMA. We cannot + * guarantee that pages in the KFENCE pool will have the requested + * properties (e.g. reside in DMAable memory). + */ + if ((flags & GFP_ZONEMASK) || + (s->flags & (SLAB_CACHE_DMA | SLAB_CACHE_DMA32))) + return NULL; + + /* + * allocation_gate only needs to become non-zero, so it doesn't make + * sense to continue writing to it and pay the associated contention + * cost, in case we have a large number of concurrent allocations. + */ + if (atomic_read(&kfence_allocation_gate) || atomic_inc_return(&kfence_allocation_gate) > 1) + return NULL; +#ifdef CONFIG_KFENCE_STATIC_KEYS + /* + * waitqueue_active() is fully ordered after the update of + * kfence_allocation_gate per atomic_inc_return(). + */ + if (waitqueue_active(&allocation_wait)) { + /* + * Calling wake_up() here may deadlock when allocations happen + * from within timer code. Use an irq_work to defer it. + */ + irq_work_queue(&wake_up_kfence_timer_work); + } +#endif + + if (!READ_ONCE(kfence_enabled)) + return NULL; + + return kfence_guarded_alloc(s, size, flags); +} + +size_t kfence_ksize(const void *addr) +{ + const struct kfence_metadata *meta = addr_to_metadata((unsigned long)addr); + + /* + * Read locklessly -- if there is a race with __kfence_alloc(), this is + * either a use-after-free or invalid access. + */ + return meta ? meta->size : 0; +} + +void *kfence_object_start(const void *addr) +{ + const struct kfence_metadata *meta = addr_to_metadata((unsigned long)addr); + + /* + * Read locklessly -- if there is a race with __kfence_alloc(), this is + * either a use-after-free or invalid access. + */ + return meta ? (void *)meta->addr : NULL; +} + +void __kfence_free(void *addr) +{ + struct kfence_metadata *meta = addr_to_metadata((unsigned long)addr); + + /* + * If the objects of the cache are SLAB_TYPESAFE_BY_RCU, defer freeing + * the object, as the object page may be recycled for other-typed + * objects once it has been freed. meta->cache may be NULL if the cache + * was destroyed. + */ + if (unlikely(meta->cache && (meta->cache->flags & SLAB_TYPESAFE_BY_RCU))) + call_rcu(&meta->rcu_head, rcu_guarded_free); + else + kfence_guarded_free(addr, meta, false); +} + +bool kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs) +{ + const int page_index = (addr - (unsigned long)__kfence_pool) / PAGE_SIZE; + struct kfence_metadata *to_report = NULL; + enum kfence_error_type error_type; + unsigned long flags; + + if (!is_kfence_address((void *)addr)) + return false; + + if (!READ_ONCE(kfence_enabled)) /* If disabled at runtime ... */ + return kfence_unprotect(addr); /* ... unprotect and proceed. */ + + atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]); + + if (page_index % 2) { + /* This is a redzone, report a buffer overflow. */ + struct kfence_metadata *meta; + int distance = 0; + + meta = addr_to_metadata(addr - PAGE_SIZE); + if (meta && READ_ONCE(meta->state) == KFENCE_OBJECT_ALLOCATED) { + to_report = meta; + /* Data race ok; distance calculation approximate. */ + distance = addr - data_race(meta->addr + meta->size); + } + + meta = addr_to_metadata(addr + PAGE_SIZE); + if (meta && READ_ONCE(meta->state) == KFENCE_OBJECT_ALLOCATED) { + /* Data race ok; distance calculation approximate. */ + if (!to_report || distance > data_race(meta->addr) - addr) + to_report = meta; + } + + if (!to_report) + goto out; + + raw_spin_lock_irqsave(&to_report->lock, flags); + to_report->unprotected_page = addr; + error_type = KFENCE_ERROR_OOB; + + /* + * If the object was freed before we took the look we can still + * report this as an OOB -- the report will simply show the + * stacktrace of the free as well. + */ + } else { + to_report = addr_to_metadata(addr); + if (!to_report) + goto out; + + raw_spin_lock_irqsave(&to_report->lock, flags); + error_type = KFENCE_ERROR_UAF; + /* + * We may race with __kfence_alloc(), and it is possible that a + * freed object may be reallocated. We simply report this as a + * use-after-free, with the stack trace showing the place where + * the object was re-allocated. + */ + } + +out: + if (to_report) { + kfence_report_error(addr, is_write, regs, to_report, error_type); + raw_spin_unlock_irqrestore(&to_report->lock, flags); + } else { + /* This may be a UAF or OOB access, but we can't be sure. */ + kfence_report_error(addr, is_write, regs, NULL, KFENCE_ERROR_INVALID); + } + + return kfence_unprotect(addr); /* Unprotect and let access proceed. */ +} diff --git a/mm/kfence/kfence.h b/mm/kfence/kfence.h new file mode 100644 index 000000000000..24065321ff8a --- /dev/null +++ b/mm/kfence/kfence.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Kernel Electric-Fence (KFENCE). For more info please see + * Documentation/dev-tools/kfence.rst. + * + * Copyright (C) 2020, Google LLC. + */ + +#ifndef MM_KFENCE_KFENCE_H +#define MM_KFENCE_KFENCE_H + +#include +#include +#include +#include + +#include "../slab.h" /* for struct kmem_cache */ + +/* + * Get the canary byte pattern for @addr. Use a pattern that varies based on the + * lower 3 bits of the address, to detect memory corruptions with higher + * probability, where similar constants are used. + */ +#define KFENCE_CANARY_PATTERN(addr) ((u8)0xaa ^ (u8)((unsigned long)(addr) & 0x7)) + +/* Maximum stack depth for reports. */ +#define KFENCE_STACK_DEPTH 64 + +/* KFENCE object states. */ +enum kfence_object_state { + KFENCE_OBJECT_UNUSED, /* Object is unused. */ + KFENCE_OBJECT_ALLOCATED, /* Object is currently allocated. */ + KFENCE_OBJECT_FREED, /* Object was allocated, and then freed. */ +}; + +/* Alloc/free tracking information. */ +struct kfence_track { + pid_t pid; + int num_stack_entries; + unsigned long stack_entries[KFENCE_STACK_DEPTH]; +}; + +/* KFENCE metadata per guarded allocation. */ +struct kfence_metadata { + struct list_head list; /* Freelist node; access under kfence_freelist_lock. */ + struct rcu_head rcu_head; /* For delayed freeing. */ + + /* + * Lock protecting below data; to ensure consistency of the below data, + * since the following may execute concurrently: __kfence_alloc(), + * __kfence_free(), kfence_handle_page_fault(). However, note that we + * cannot grab the same metadata off the freelist twice, and multiple + * __kfence_alloc() cannot run concurrently on the same metadata. + */ + raw_spinlock_t lock; + + /* The current state of the object; see above. */ + enum kfence_object_state state; + + /* + * Allocated object address; cannot be calculated from size, because of + * alignment requirements. + * + * Invariant: ALIGN_DOWN(addr, PAGE_SIZE) is constant. + */ + unsigned long addr; + + /* + * The size of the original allocation. + */ + size_t size; + + /* + * The kmem_cache cache of the last allocation; NULL if never allocated + * or the cache has already been destroyed. + */ + struct kmem_cache *cache; + + /* + * In case of an invalid access, the page that was unprotected; we + * optimistically only store one address. + */ + unsigned long unprotected_page; + + /* Allocation and free stack information. */ + struct kfence_track alloc_track; + struct kfence_track free_track; +}; + +extern struct kfence_metadata kfence_metadata[CONFIG_KFENCE_NUM_OBJECTS]; + +/* KFENCE error types for report generation. */ +enum kfence_error_type { + KFENCE_ERROR_OOB, /* Detected a out-of-bounds access. */ + KFENCE_ERROR_UAF, /* Detected a use-after-free access. */ + KFENCE_ERROR_CORRUPTION, /* Detected a memory corruption on free. */ + KFENCE_ERROR_INVALID, /* Invalid access of unknown type. */ + KFENCE_ERROR_INVALID_FREE, /* Invalid free. */ +}; + +void kfence_report_error(unsigned long address, bool is_write, struct pt_regs *regs, + const struct kfence_metadata *meta, enum kfence_error_type type); + +void kfence_print_object(struct seq_file *seq, const struct kfence_metadata *meta); + +#endif /* MM_KFENCE_KFENCE_H */ diff --git a/mm/kfence/kfence_test.c b/mm/kfence/kfence_test.c new file mode 100644 index 000000000000..b959548a47ce --- /dev/null +++ b/mm/kfence/kfence_test.c @@ -0,0 +1,847 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for KFENCE memory safety error detector. Since the interface with + * which KFENCE's reports are obtained is via the console, this is the output we + * should verify. For each test case checks the presence (or absence) of + * generated reports. Relies on 'console' tracepoint to capture reports as they + * appear in the kernel log. + * + * Copyright (C) 2020, Google LLC. + * Author: Alexander Potapenko + * Marco Elver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kfence.h" + +/* Report as observed from console. */ +static struct { + spinlock_t lock; + int nlines; + char lines[2][256]; +} observed = { + .lock = __SPIN_LOCK_UNLOCKED(observed.lock), +}; + +/* Probe for console output: obtains observed lines of interest. */ +static void probe_console(void *ignore, const char *buf, size_t len) +{ + unsigned long flags; + int nlines; + + spin_lock_irqsave(&observed.lock, flags); + nlines = observed.nlines; + + if (strnstr(buf, "BUG: KFENCE: ", len) && strnstr(buf, "test_", len)) { + /* + * KFENCE report and related to the test. + * + * The provided @buf is not NUL-terminated; copy no more than + * @len bytes and let strscpy() add the missing NUL-terminator. + */ + strscpy(observed.lines[0], buf, min(len + 1, sizeof(observed.lines[0]))); + nlines = 1; + } else if (nlines == 1 && (strnstr(buf, "at 0x", len) || strnstr(buf, "of 0x", len))) { + strscpy(observed.lines[nlines++], buf, min(len + 1, sizeof(observed.lines[0]))); + } + + WRITE_ONCE(observed.nlines, nlines); /* Publish new nlines. */ + spin_unlock_irqrestore(&observed.lock, flags); +} + +/* Check if a report related to the test exists. */ +static bool report_available(void) +{ + return READ_ONCE(observed.nlines) == ARRAY_SIZE(observed.lines); +} + +/* Information we expect in a report. */ +struct expect_report { + enum kfence_error_type type; /* The type or error. */ + void *fn; /* Function pointer to expected function where access occurred. */ + char *addr; /* Address at which the bad access occurred. */ + bool is_write; /* Is access a write. */ +}; + +static const char *get_access_type(const struct expect_report *r) +{ + return r->is_write ? "write" : "read"; +} + +/* Check observed report matches information in @r. */ +static bool report_matches(const struct expect_report *r) +{ + bool ret = false; + unsigned long flags; + typeof(observed.lines) expect; + const char *end; + char *cur; + + /* Doubled-checked locking. */ + if (!report_available()) + return false; + + /* Generate expected report contents. */ + + /* Title */ + cur = expect[0]; + end = &expect[0][sizeof(expect[0]) - 1]; + switch (r->type) { + case KFENCE_ERROR_OOB: + cur += scnprintf(cur, end - cur, "BUG: KFENCE: out-of-bounds %s", + get_access_type(r)); + break; + case KFENCE_ERROR_UAF: + cur += scnprintf(cur, end - cur, "BUG: KFENCE: use-after-free %s", + get_access_type(r)); + break; + case KFENCE_ERROR_CORRUPTION: + cur += scnprintf(cur, end - cur, "BUG: KFENCE: memory corruption"); + break; + case KFENCE_ERROR_INVALID: + cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid %s", + get_access_type(r)); + break; + case KFENCE_ERROR_INVALID_FREE: + cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid free"); + break; + } + + scnprintf(cur, end - cur, " in %pS", r->fn); + /* The exact offset won't match, remove it; also strip module name. */ + cur = strchr(expect[0], '+'); + if (cur) + *cur = '\0'; + + /* Access information */ + cur = expect[1]; + end = &expect[1][sizeof(expect[1]) - 1]; + + switch (r->type) { + case KFENCE_ERROR_OOB: + cur += scnprintf(cur, end - cur, "Out-of-bounds %s at", get_access_type(r)); + break; + case KFENCE_ERROR_UAF: + cur += scnprintf(cur, end - cur, "Use-after-free %s at", get_access_type(r)); + break; + case KFENCE_ERROR_CORRUPTION: + cur += scnprintf(cur, end - cur, "Corrupted memory at"); + break; + case KFENCE_ERROR_INVALID: + cur += scnprintf(cur, end - cur, "Invalid %s at", get_access_type(r)); + break; + case KFENCE_ERROR_INVALID_FREE: + cur += scnprintf(cur, end - cur, "Invalid free of"); + break; + } + + cur += scnprintf(cur, end - cur, " 0x%p", (void *)r->addr); + + spin_lock_irqsave(&observed.lock, flags); + if (!report_available()) + goto out; /* A new report is being captured. */ + + /* Finally match expected output to what we actually observed. */ + ret = strstr(observed.lines[0], expect[0]) && strstr(observed.lines[1], expect[1]); +out: + spin_unlock_irqrestore(&observed.lock, flags); + return ret; +} + +/* ===== Test cases ===== */ + +#define TEST_PRIV_WANT_MEMCACHE ((void *)1) + +/* Cache used by tests; if NULL, allocate from kmalloc instead. */ +static struct kmem_cache *test_cache; + +static size_t setup_test_cache(struct kunit *test, size_t size, slab_flags_t flags, + void (*ctor)(void *)) +{ + if (test->priv != TEST_PRIV_WANT_MEMCACHE) + return size; + + kunit_info(test, "%s: size=%zu, ctor=%ps\n", __func__, size, ctor); + + /* + * Use SLAB_NOLEAKTRACE to prevent merging with existing caches. Any + * other flag in SLAB_NEVER_MERGE also works. Use SLAB_ACCOUNT to + * allocate via memcg, if enabled. + */ + flags |= SLAB_NOLEAKTRACE | SLAB_ACCOUNT; + test_cache = kmem_cache_create("test", size, 1, flags, ctor); + KUNIT_ASSERT_TRUE_MSG(test, test_cache, "could not create cache"); + + return size; +} + +static void test_cache_destroy(void) +{ + if (!test_cache) + return; + + kmem_cache_destroy(test_cache); + test_cache = NULL; +} + +static inline size_t kmalloc_cache_alignment(size_t size) +{ + return kmalloc_caches[kmalloc_type(GFP_KERNEL)][kmalloc_index(size)]->align; +} + +/* Must always inline to match stack trace against caller. */ +static __always_inline void test_free(void *ptr) +{ + if (test_cache) + kmem_cache_free(test_cache, ptr); + else + kfree(ptr); +} + +/* + * If this should be a KFENCE allocation, and on which side the allocation and + * the closest guard page should be. + */ +enum allocation_policy { + ALLOCATE_ANY, /* KFENCE, any side. */ + ALLOCATE_LEFT, /* KFENCE, left side of page. */ + ALLOCATE_RIGHT, /* KFENCE, right side of page. */ + ALLOCATE_NONE, /* No KFENCE allocation. */ +}; + +/* + * Try to get a guarded allocation from KFENCE. Uses either kmalloc() or the + * current test_cache if set up. + */ +static void *test_alloc(struct kunit *test, size_t size, gfp_t gfp, enum allocation_policy policy) +{ + void *alloc; + unsigned long timeout, resched_after; + const char *policy_name; + + switch (policy) { + case ALLOCATE_ANY: + policy_name = "any"; + break; + case ALLOCATE_LEFT: + policy_name = "left"; + break; + case ALLOCATE_RIGHT: + policy_name = "right"; + break; + case ALLOCATE_NONE: + policy_name = "none"; + break; + } + + kunit_info(test, "%s: size=%zu, gfp=%x, policy=%s, cache=%i\n", __func__, size, gfp, + policy_name, !!test_cache); + + /* + * 100x the sample interval should be more than enough to ensure we get + * a KFENCE allocation eventually. + */ + timeout = jiffies + msecs_to_jiffies(100 * CONFIG_KFENCE_SAMPLE_INTERVAL); + /* + * Especially for non-preemption kernels, ensure the allocation-gate + * timer can catch up: after @resched_after, every failed allocation + * attempt yields, to ensure the allocation-gate timer is scheduled. + */ + resched_after = jiffies + msecs_to_jiffies(CONFIG_KFENCE_SAMPLE_INTERVAL); + do { + if (test_cache) + alloc = kmem_cache_alloc(test_cache, gfp); + else + alloc = kmalloc(size, gfp); + + if (is_kfence_address(alloc)) { + if (policy == ALLOCATE_ANY) + return alloc; + if (policy == ALLOCATE_LEFT && IS_ALIGNED((unsigned long)alloc, PAGE_SIZE)) + return alloc; + if (policy == ALLOCATE_RIGHT && + !IS_ALIGNED((unsigned long)alloc, PAGE_SIZE)) + return alloc; + } else if (policy == ALLOCATE_NONE) + return alloc; + + test_free(alloc); + + if (time_after(jiffies, resched_after)) + cond_resched(); + } while (time_before(jiffies, timeout)); + + KUNIT_ASSERT_TRUE_MSG(test, false, "failed to allocate from KFENCE"); + return NULL; /* Unreachable. */ +} + +static void test_out_of_bounds_read(struct kunit *test) +{ + size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_OOB, + .fn = test_out_of_bounds_read, + .is_write = false, + }; + char *buf; + + setup_test_cache(test, size, 0, NULL); + + /* + * If we don't have our own cache, adjust based on alignment, so that we + * actually access guard pages on either side. + */ + if (!test_cache) + size = kmalloc_cache_alignment(size); + + /* Test both sides. */ + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT); + expect.addr = buf - 1; + READ_ONCE(*expect.addr); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + test_free(buf); + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT); + expect.addr = buf + size; + READ_ONCE(*expect.addr); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + test_free(buf); +} + +static void test_out_of_bounds_write(struct kunit *test) +{ + size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_OOB, + .fn = test_out_of_bounds_write, + .is_write = true, + }; + char *buf; + + setup_test_cache(test, size, 0, NULL); + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT); + expect.addr = buf - 1; + WRITE_ONCE(*expect.addr, 42); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + test_free(buf); +} + +static void test_use_after_free_read(struct kunit *test) +{ + const size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_UAF, + .fn = test_use_after_free_read, + .is_write = false, + }; + + setup_test_cache(test, size, 0, NULL); + expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + test_free(expect.addr); + READ_ONCE(*expect.addr); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +static void test_double_free(struct kunit *test) +{ + const size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_INVALID_FREE, + .fn = test_double_free, + }; + + setup_test_cache(test, size, 0, NULL); + expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + test_free(expect.addr); + test_free(expect.addr); /* Double-free. */ + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +static void test_invalid_addr_free(struct kunit *test) +{ + const size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_INVALID_FREE, + .fn = test_invalid_addr_free, + }; + char *buf; + + setup_test_cache(test, size, 0, NULL); + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + expect.addr = buf + 1; /* Free on invalid address. */ + test_free(expect.addr); /* Invalid address free. */ + test_free(buf); /* No error. */ + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +static void test_corruption(struct kunit *test) +{ + size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_CORRUPTION, + .fn = test_corruption, + }; + char *buf; + + setup_test_cache(test, size, 0, NULL); + + /* Test both sides. */ + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT); + expect.addr = buf + size; + WRITE_ONCE(*expect.addr, 42); + test_free(buf); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT); + expect.addr = buf - 1; + WRITE_ONCE(*expect.addr, 42); + test_free(buf); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +/* + * KFENCE is unable to detect an OOB if the allocation's alignment requirements + * leave a gap between the object and the guard page. Specifically, an + * allocation of e.g. 73 bytes is aligned on 8 and 128 bytes for SLUB or SLAB + * respectively. Therefore it is impossible for the allocated object to + * contiguously line up with the right guard page. + * + * However, we test that an access to memory beyond the gap results in KFENCE + * detecting an OOB access. + */ +static void test_kmalloc_aligned_oob_read(struct kunit *test) +{ + const size_t size = 73; + const size_t align = kmalloc_cache_alignment(size); + struct expect_report expect = { + .type = KFENCE_ERROR_OOB, + .fn = test_kmalloc_aligned_oob_read, + .is_write = false, + }; + char *buf; + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT); + + /* + * The object is offset to the right, so there won't be an OOB to the + * left of it. + */ + READ_ONCE(*(buf - 1)); + KUNIT_EXPECT_FALSE(test, report_available()); + + /* + * @buf must be aligned on @align, therefore buf + size belongs to the + * same page -> no OOB. + */ + READ_ONCE(*(buf + size)); + KUNIT_EXPECT_FALSE(test, report_available()); + + /* Overflowing by @align bytes will result in an OOB. */ + expect.addr = buf + size + align; + READ_ONCE(*expect.addr); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + + test_free(buf); +} + +static void test_kmalloc_aligned_oob_write(struct kunit *test) +{ + const size_t size = 73; + struct expect_report expect = { + .type = KFENCE_ERROR_CORRUPTION, + .fn = test_kmalloc_aligned_oob_write, + }; + char *buf; + + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT); + /* + * The object is offset to the right, so we won't get a page + * fault immediately after it. + */ + expect.addr = buf + size; + WRITE_ONCE(*expect.addr, READ_ONCE(*expect.addr) + 1); + KUNIT_EXPECT_FALSE(test, report_available()); + test_free(buf); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +/* Test cache shrinking and destroying with KFENCE. */ +static void test_shrink_memcache(struct kunit *test) +{ + const size_t size = 32; + void *buf; + + setup_test_cache(test, size, 0, NULL); + KUNIT_EXPECT_TRUE(test, test_cache); + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + kmem_cache_shrink(test_cache); + test_free(buf); + + KUNIT_EXPECT_FALSE(test, report_available()); +} + +static void ctor_set_x(void *obj) +{ + /* Every object has at least 8 bytes. */ + memset(obj, 'x', 8); +} + +/* Ensure that SL*B does not modify KFENCE objects on bulk free. */ +static void test_free_bulk(struct kunit *test) +{ + int iter; + + for (iter = 0; iter < 5; iter++) { + const size_t size = setup_test_cache(test, 8 + prandom_u32_max(300), 0, + (iter & 1) ? ctor_set_x : NULL); + void *objects[] = { + test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT), + test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE), + test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT), + test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE), + test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE), + }; + + kmem_cache_free_bulk(test_cache, ARRAY_SIZE(objects), objects); + KUNIT_ASSERT_FALSE(test, report_available()); + test_cache_destroy(); + } +} + +/* Test init-on-free works. */ +static void test_init_on_free(struct kunit *test) +{ + const size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_UAF, + .fn = test_init_on_free, + .is_write = false, + }; + int i; + + if (!IS_ENABLED(CONFIG_INIT_ON_FREE_DEFAULT_ON)) + return; + /* Assume it hasn't been disabled on command line. */ + + setup_test_cache(test, size, 0, NULL); + expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + for (i = 0; i < size; i++) + expect.addr[i] = i + 1; + test_free(expect.addr); + + for (i = 0; i < size; i++) { + /* + * This may fail if the page was recycled by KFENCE and then + * written to again -- this however, is near impossible with a + * default config. + */ + KUNIT_EXPECT_EQ(test, expect.addr[i], (char)0); + + if (!i) /* Only check first access to not fail test if page is ever re-protected. */ + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); + } +} + +/* Ensure that constructors work properly. */ +static void test_memcache_ctor(struct kunit *test) +{ + const size_t size = 32; + char *buf; + int i; + + setup_test_cache(test, size, 0, ctor_set_x); + buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + + for (i = 0; i < 8; i++) + KUNIT_EXPECT_EQ(test, buf[i], (char)'x'); + + test_free(buf); + + KUNIT_EXPECT_FALSE(test, report_available()); +} + +/* Test that memory is zeroed if requested. */ +static void test_gfpzero(struct kunit *test) +{ + const size_t size = PAGE_SIZE; /* PAGE_SIZE so we can use ALLOCATE_ANY. */ + char *buf1, *buf2; + int i; + + if (CONFIG_KFENCE_SAMPLE_INTERVAL > 100) { + kunit_warn(test, "skipping ... would take too long\n"); + return; + } + + setup_test_cache(test, size, 0, NULL); + buf1 = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + for (i = 0; i < size; i++) + buf1[i] = i + 1; + test_free(buf1); + + /* Try to get same address again -- this can take a while. */ + for (i = 0;; i++) { + buf2 = test_alloc(test, size, GFP_KERNEL | __GFP_ZERO, ALLOCATE_ANY); + if (buf1 == buf2) + break; + test_free(buf2); + + if (i == CONFIG_KFENCE_NUM_OBJECTS) { + kunit_warn(test, "giving up ... cannot get same object back\n"); + return; + } + } + + for (i = 0; i < size; i++) + KUNIT_EXPECT_EQ(test, buf2[i], (char)0); + + test_free(buf2); + + KUNIT_EXPECT_FALSE(test, report_available()); +} + +static void test_invalid_access(struct kunit *test) +{ + const struct expect_report expect = { + .type = KFENCE_ERROR_INVALID, + .fn = test_invalid_access, + .addr = &__kfence_pool[10], + .is_write = false, + }; + + READ_ONCE(__kfence_pool[10]); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +/* Test SLAB_TYPESAFE_BY_RCU works. */ +static void test_memcache_typesafe_by_rcu(struct kunit *test) +{ + const size_t size = 32; + struct expect_report expect = { + .type = KFENCE_ERROR_UAF, + .fn = test_memcache_typesafe_by_rcu, + .is_write = false, + }; + + setup_test_cache(test, size, SLAB_TYPESAFE_BY_RCU, NULL); + KUNIT_EXPECT_TRUE(test, test_cache); /* Want memcache. */ + + expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY); + *expect.addr = 42; + + rcu_read_lock(); + test_free(expect.addr); + KUNIT_EXPECT_EQ(test, *expect.addr, (char)42); + /* + * Up to this point, memory should not have been freed yet, and + * therefore there should be no KFENCE report from the above access. + */ + rcu_read_unlock(); + + /* Above access to @expect.addr should not have generated a report! */ + KUNIT_EXPECT_FALSE(test, report_available()); + + /* Only after rcu_barrier() is the memory guaranteed to be freed. */ + rcu_barrier(); + + /* Expect use-after-free. */ + KUNIT_EXPECT_EQ(test, *expect.addr, (char)42); + KUNIT_EXPECT_TRUE(test, report_matches(&expect)); +} + +/* Test krealloc(). */ +static void test_krealloc(struct kunit *test) +{ + const size_t size = 32; + const struct expect_report expect = { + .type = KFENCE_ERROR_UAF, + .fn = test_krealloc, + .addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY), + .is_write = false, + }; + char *buf = expect.addr; + int i; + + KUNIT_EXPECT_FALSE(test, test_cache); + KUNIT_EXPECT_EQ(test, ksize(buf), size); /* Precise size match after KFENCE alloc. */ + for (i = 0; i < size; i++) + buf[i] = i + 1; + + /* Check that we successfully change the size. */ + buf = krealloc(buf, size * 3, GFP_KERNEL); /* Grow. */ + /* Note: Might no longer be a KFENCE alloc. */ + KUNIT_EXPECT_GE(test, ksize(buf), size * 3); + for (i = 0; i < size; i++) + KUNIT_EXPECT_EQ(test, buf[i], (char)(i + 1)); + for (; i < size * 3; i++) /* Fill to extra bytes. */ + buf[i] = i + 1; + + buf = krealloc(buf, size * 2, GFP_KERNEL); /* Shrink. */ + KUNIT_EXPECT_GE(test, ksize(buf), size * 2); + for (i = 0; i < size * 2; i++) + KUNIT_EXPECT_EQ(test, buf[i], (char)(i + 1)); + + buf = krealloc(buf, 0, GFP_KERNEL); /* Free. */ + KUNIT_EXPECT_EQ(test, (unsigned long)buf, (unsigned long)ZERO_SIZE_PTR); + KUNIT_ASSERT_FALSE(test, report_available()); /* No reports yet! */ + + READ_ONCE(*expect.addr); /* Ensure krealloc() actually freed earlier KFENCE object. */ + KUNIT_ASSERT_TRUE(test, report_matches(&expect)); +} + +/* Test that some objects from a bulk allocation belong to KFENCE pool. */ +static void test_memcache_alloc_bulk(struct kunit *test) +{ + const size_t size = 32; + bool pass = false; + unsigned long timeout; + + setup_test_cache(test, size, 0, NULL); + KUNIT_EXPECT_TRUE(test, test_cache); /* Want memcache. */ + /* + * 100x the sample interval should be more than enough to ensure we get + * a KFENCE allocation eventually. + */ + timeout = jiffies + msecs_to_jiffies(100 * CONFIG_KFENCE_SAMPLE_INTERVAL); + do { + void *objects[100]; + int i, num = kmem_cache_alloc_bulk(test_cache, GFP_ATOMIC, ARRAY_SIZE(objects), + objects); + if (!num) + continue; + for (i = 0; i < ARRAY_SIZE(objects); i++) { + if (is_kfence_address(objects[i])) { + pass = true; + break; + } + } + kmem_cache_free_bulk(test_cache, num, objects); + /* + * kmem_cache_alloc_bulk() disables interrupts, and calling it + * in a tight loop may not give KFENCE a chance to switch the + * static branch. Call cond_resched() to let KFENCE chime in. + */ + cond_resched(); + } while (!pass && time_before(jiffies, timeout)); + + KUNIT_EXPECT_TRUE(test, pass); + KUNIT_EXPECT_FALSE(test, report_available()); +} + +/* + * KUnit does not provide a way to provide arguments to tests, and we encode + * additional info in the name. Set up 2 tests per test case, one using the + * default allocator, and another using a custom memcache (suffix '-memcache'). + */ +#define KFENCE_KUNIT_CASE(test_name) \ + { .run_case = test_name, .name = #test_name }, \ + { .run_case = test_name, .name = #test_name "-memcache" } + +static struct kunit_case kfence_test_cases[] = { + KFENCE_KUNIT_CASE(test_out_of_bounds_read), + KFENCE_KUNIT_CASE(test_out_of_bounds_write), + KFENCE_KUNIT_CASE(test_use_after_free_read), + KFENCE_KUNIT_CASE(test_double_free), + KFENCE_KUNIT_CASE(test_invalid_addr_free), + KFENCE_KUNIT_CASE(test_corruption), + KFENCE_KUNIT_CASE(test_free_bulk), + KFENCE_KUNIT_CASE(test_init_on_free), + KUNIT_CASE(test_kmalloc_aligned_oob_read), + KUNIT_CASE(test_kmalloc_aligned_oob_write), + KUNIT_CASE(test_shrink_memcache), + KUNIT_CASE(test_memcache_ctor), + KUNIT_CASE(test_invalid_access), + KUNIT_CASE(test_gfpzero), + KUNIT_CASE(test_memcache_typesafe_by_rcu), + KUNIT_CASE(test_krealloc), + KUNIT_CASE(test_memcache_alloc_bulk), + {}, +}; + +/* ===== End test cases ===== */ + +static int test_init(struct kunit *test) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&observed.lock, flags); + for (i = 0; i < ARRAY_SIZE(observed.lines); i++) + observed.lines[i][0] = '\0'; + observed.nlines = 0; + spin_unlock_irqrestore(&observed.lock, flags); + + /* Any test with 'memcache' in its name will want a memcache. */ + if (strstr(test->name, "memcache")) + test->priv = TEST_PRIV_WANT_MEMCACHE; + else + test->priv = NULL; + + return 0; +} + +static void test_exit(struct kunit *test) +{ + test_cache_destroy(); +} + +static struct kunit_suite kfence_test_suite = { + .name = "kfence", + .test_cases = kfence_test_cases, + .init = test_init, + .exit = test_exit, +}; +static struct kunit_suite *kfence_test_suites[] = { &kfence_test_suite, NULL }; + +static void register_tracepoints(struct tracepoint *tp, void *ignore) +{ + check_trace_callback_type_console(probe_console); + if (!strcmp(tp->name, "console")) + WARN_ON(tracepoint_probe_register(tp, probe_console, NULL)); +} + +static void unregister_tracepoints(struct tracepoint *tp, void *ignore) +{ + if (!strcmp(tp->name, "console")) + tracepoint_probe_unregister(tp, probe_console, NULL); +} + +/* + * We only want to do tracepoints setup and teardown once, therefore we have to + * customize the init and exit functions and cannot rely on kunit_test_suite(). + */ +static int __init kfence_test_init(void) +{ + /* + * Because we want to be able to build the test as a module, we need to + * iterate through all known tracepoints, since the static registration + * won't work here. + */ + for_each_kernel_tracepoint(register_tracepoints, NULL); + return __kunit_test_suites_init(kfence_test_suites); +} + +static void kfence_test_exit(void) +{ + __kunit_test_suites_exit(kfence_test_suites); + for_each_kernel_tracepoint(unregister_tracepoints, NULL); + tracepoint_synchronize_unregister(); +} + +late_initcall(kfence_test_init); +module_exit(kfence_test_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Potapenko , Marco Elver "); diff --git a/mm/kfence/report.c b/mm/kfence/report.c new file mode 100644 index 000000000000..e3f71451ad9e --- /dev/null +++ b/mm/kfence/report.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KFENCE reporting. + * + * Copyright (C) 2020, Google LLC. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kfence.h" + +/* May be overridden by . */ +#ifndef ARCH_FUNC_PREFIX +#define ARCH_FUNC_PREFIX "" +#endif + +extern bool no_hash_pointers; + +/* Helper function to either print to a seq_file or to console. */ +__printf(2, 3) +static void seq_con_printf(struct seq_file *seq, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (seq) + seq_vprintf(seq, fmt, args); + else + vprintk(fmt, args); + va_end(args); +} + +/* + * Get the number of stack entries to skip to get out of MM internals. @type is + * optional, and if set to NULL, assumes an allocation or free stack. + */ +static int get_stack_skipnr(const unsigned long stack_entries[], int num_entries, + const enum kfence_error_type *type) +{ + char buf[64]; + int skipnr, fallback = 0; + + if (type) { + /* Depending on error type, find different stack entries. */ + switch (*type) { + case KFENCE_ERROR_UAF: + case KFENCE_ERROR_OOB: + case KFENCE_ERROR_INVALID: + /* + * kfence_handle_page_fault() may be called with pt_regs + * set to NULL; in that case we'll simply show the full + * stack trace. + */ + return 0; + case KFENCE_ERROR_CORRUPTION: + case KFENCE_ERROR_INVALID_FREE: + break; + } + } + + for (skipnr = 0; skipnr < num_entries; skipnr++) { + int len = scnprintf(buf, sizeof(buf), "%ps", (void *)stack_entries[skipnr]); + + if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfence_") || + str_has_prefix(buf, ARCH_FUNC_PREFIX "__kfence_") || + !strncmp(buf, ARCH_FUNC_PREFIX "__slab_free", len)) { + /* + * In case of tail calls from any of the below + * to any of the above. + */ + fallback = skipnr + 1; + } + + /* Also the *_bulk() variants by only checking prefixes. */ + if (str_has_prefix(buf, ARCH_FUNC_PREFIX "kfree") || + str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_free") || + str_has_prefix(buf, ARCH_FUNC_PREFIX "__kmalloc") || + str_has_prefix(buf, ARCH_FUNC_PREFIX "kmem_cache_alloc")) + goto found; + } + if (fallback < num_entries) + return fallback; +found: + skipnr++; + return skipnr < num_entries ? skipnr : 0; +} + +static void kfence_print_stack(struct seq_file *seq, const struct kfence_metadata *meta, + bool show_alloc) +{ + const struct kfence_track *track = show_alloc ? &meta->alloc_track : &meta->free_track; + + if (track->num_stack_entries) { + /* Skip allocation/free internals stack. */ + int i = get_stack_skipnr(track->stack_entries, track->num_stack_entries, NULL); + + /* stack_trace_seq_print() does not exist; open code our own. */ + for (; i < track->num_stack_entries; i++) + seq_con_printf(seq, " %pS\n", (void *)track->stack_entries[i]); + } else { + seq_con_printf(seq, " no %s stack\n", show_alloc ? "allocation" : "deallocation"); + } +} + +void kfence_print_object(struct seq_file *seq, const struct kfence_metadata *meta) +{ + const int size = abs(meta->size); + const unsigned long start = meta->addr; + const struct kmem_cache *const cache = meta->cache; + + lockdep_assert_held(&meta->lock); + + if (meta->state == KFENCE_OBJECT_UNUSED) { + seq_con_printf(seq, "kfence-#%td unused\n", meta - kfence_metadata); + return; + } + + seq_con_printf(seq, + "kfence-#%td [0x%p-0x%p" + ", size=%d, cache=%s] allocated by task %d:\n", + meta - kfence_metadata, (void *)start, (void *)(start + size - 1), size, + (cache && cache->name) ? cache->name : "", meta->alloc_track.pid); + kfence_print_stack(seq, meta, true); + + if (meta->state == KFENCE_OBJECT_FREED) { + seq_con_printf(seq, "\nfreed by task %d:\n", meta->free_track.pid); + kfence_print_stack(seq, meta, false); + } +} + +/* + * Show bytes at @addr that are different from the expected canary values, up to + * @max_bytes. + */ +static void print_diff_canary(unsigned long address, size_t bytes_to_show, + const struct kfence_metadata *meta) +{ + const unsigned long show_until_addr = address + bytes_to_show; + const u8 *cur, *end; + + /* Do not show contents of object nor read into following guard page. */ + end = (const u8 *)(address < meta->addr ? min(show_until_addr, meta->addr) + : min(show_until_addr, PAGE_ALIGN(address))); + + pr_cont("["); + for (cur = (const u8 *)address; cur < end; cur++) { + if (*cur == KFENCE_CANARY_PATTERN(cur)) + pr_cont(" ."); + else if (no_hash_pointers) + pr_cont(" 0x%02x", *cur); + else /* Do not leak kernel memory in non-debug builds. */ + pr_cont(" !"); + } + pr_cont(" ]"); +} + +static const char *get_access_type(bool is_write) +{ + return is_write ? "write" : "read"; +} + +void kfence_report_error(unsigned long address, bool is_write, struct pt_regs *regs, + const struct kfence_metadata *meta, enum kfence_error_type type) +{ + unsigned long stack_entries[KFENCE_STACK_DEPTH] = { 0 }; + const ptrdiff_t object_index = meta ? meta - kfence_metadata : -1; + int num_stack_entries; + int skipnr = 0; + + if (regs) { + num_stack_entries = stack_trace_save_regs(regs, stack_entries, KFENCE_STACK_DEPTH, 0); + } else { + num_stack_entries = stack_trace_save(stack_entries, KFENCE_STACK_DEPTH, 1); + skipnr = get_stack_skipnr(stack_entries, num_stack_entries, &type); + } + + /* Require non-NULL meta, except if KFENCE_ERROR_INVALID. */ + if (WARN_ON(type != KFENCE_ERROR_INVALID && !meta)) + return; + + if (meta) + lockdep_assert_held(&meta->lock); + /* + * Because we may generate reports in printk-unfriendly parts of the + * kernel, such as scheduler code, the use of printk() could deadlock. + * Until such time that all printing code here is safe in all parts of + * the kernel, accept the risk, and just get our message out (given the + * system might already behave unpredictably due to the memory error). + * As such, also disable lockdep to hide warnings, and avoid disabling + * lockdep for the rest of the kernel. + */ + lockdep_off(); + + pr_err("==================================================================\n"); + /* Print report header. */ + switch (type) { + case KFENCE_ERROR_OOB: { + const bool left_of_object = address < meta->addr; + + pr_err("BUG: KFENCE: out-of-bounds %s in %pS\n\n", get_access_type(is_write), + (void *)stack_entries[skipnr]); + pr_err("Out-of-bounds %s at 0x%p (%luB %s of kfence-#%td):\n", + get_access_type(is_write), (void *)address, + left_of_object ? meta->addr - address : address - meta->addr, + left_of_object ? "left" : "right", object_index); + break; + } + case KFENCE_ERROR_UAF: + pr_err("BUG: KFENCE: use-after-free %s in %pS\n\n", get_access_type(is_write), + (void *)stack_entries[skipnr]); + pr_err("Use-after-free %s at 0x%p (in kfence-#%td):\n", + get_access_type(is_write), (void *)address, object_index); + break; + case KFENCE_ERROR_CORRUPTION: + pr_err("BUG: KFENCE: memory corruption in %pS\n\n", (void *)stack_entries[skipnr]); + pr_err("Corrupted memory at 0x%p ", (void *)address); + print_diff_canary(address, 16, meta); + pr_cont(" (in kfence-#%td):\n", object_index); + break; + case KFENCE_ERROR_INVALID: + pr_err("BUG: KFENCE: invalid %s in %pS\n\n", get_access_type(is_write), + (void *)stack_entries[skipnr]); + pr_err("Invalid %s at 0x%p:\n", get_access_type(is_write), + (void *)address); + break; + case KFENCE_ERROR_INVALID_FREE: + pr_err("BUG: KFENCE: invalid free in %pS\n\n", (void *)stack_entries[skipnr]); + pr_err("Invalid free of 0x%p (in kfence-#%td):\n", (void *)address, + object_index); + break; + } + + /* Print stack trace and object info. */ + stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr, 0); + + if (meta) { + pr_err("\n"); + kfence_print_object(NULL, meta); + } + + /* Print report footer. */ + pr_err("\n"); + if (no_hash_pointers && regs) + show_regs(regs); + else + dump_stack_print_info(KERN_ERR); + trace_error_report_end(ERROR_DETECTOR_KFENCE, address); + pr_err("==================================================================\n"); + + lockdep_on(); + + if (panic_on_warn) + panic("panic_on_warn set ...\n"); + + /* We encountered a memory unsafety error, taint the kernel! */ + add_taint(TAINT_BAD_PAGE, LOCKDEP_STILL_OK); +} diff --git a/mm/kmemleak.c b/mm/kmemleak.c index a7fc6b23c37e..dce9e2a42549 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -97,6 +97,7 @@ #include #include +#include #include #include @@ -592,7 +593,7 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size, atomic_set(&object->use_count, 1); object->flags = OBJECT_ALLOCATED; object->pointer = ptr; - object->size = size; + object->size = kfence_ksize((void *)ptr) ?: size; object->excess_ref = 0; object->min_count = min_count; object->count = 0; /* white color initially */ diff --git a/mm/slab.c b/mm/slab.c index 1a75a1204ff4..a9a5fb49373d 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -100,6 +100,7 @@ #include #include #include +#include #include #include #include @@ -3219,18 +3220,28 @@ must_grow: } static __always_inline void * -slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, +slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, size_t orig_size, unsigned long caller) { unsigned long save_flags; void *ptr; int slab_node = numa_mem_id(); + struct kmem_cache *orig_cachep = cachep; flags &= gfp_allowed_mask; cachep = slab_pre_alloc_hook(cachep, flags); if (unlikely(!cachep)) return NULL; + /* + * 5.4 note: passing in original cachep to avoid problems with memcg + * accounting. Making KFENCE properly work with memcgs on older kernels + * is not worth the effort. + */ + ptr = kfence_alloc(orig_cachep, orig_size, flags); + if (unlikely(ptr)) + goto out_hooks; + cache_alloc_debugcheck_before(cachep, flags); local_irq_save(save_flags); @@ -3263,6 +3274,7 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, if (unlikely(slab_want_init_on_alloc(flags, cachep)) && ptr) memset(ptr, 0, cachep->object_size); +out_hooks: slab_post_alloc_hook(cachep, flags, 1, &ptr); return ptr; } @@ -3300,16 +3312,26 @@ __do_cache_alloc(struct kmem_cache *cachep, gfp_t flags) #endif /* CONFIG_NUMA */ static __always_inline void * -slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller) +slab_alloc(struct kmem_cache *cachep, gfp_t flags, size_t orig_size, unsigned long caller) { unsigned long save_flags; void *objp; + struct kmem_cache *orig_cachep = cachep; flags &= gfp_allowed_mask; cachep = slab_pre_alloc_hook(cachep, flags); if (unlikely(!cachep)) return NULL; + /* + * 5.4 note: passing in original cachep to avoid problems with memcg + * accounting. Making KFENCE properly work with memcgs on older kernels + * is not worth the effort. + */ + objp = kfence_alloc(orig_cachep, orig_size, flags); + if (unlikely(objp)) + goto out; + cache_alloc_debugcheck_before(cachep, flags); local_irq_save(save_flags); objp = __do_cache_alloc(cachep, flags); @@ -3320,6 +3342,7 @@ slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller) if (unlikely(slab_want_init_on_alloc(flags, cachep)) && objp) memset(objp, 0, cachep->object_size); +out: slab_post_alloc_hook(cachep, flags, 1, &objp); return objp; } @@ -3425,6 +3448,15 @@ free_done: static __always_inline void __cache_free(struct kmem_cache *cachep, void *objp, unsigned long caller) { + if (is_kfence_address(objp)) { + kmemleak_free_recursive(objp, cachep->flags); + __kfence_free(objp); + return; + } + + if (unlikely(slab_want_init_on_free(cachep))) + memset(objp, 0, cachep->object_size); + /* Put the object into the quarantine, don't touch it for now. */ if (kasan_slab_free(cachep, objp, _RET_IP_)) return; @@ -3438,8 +3470,6 @@ void ___cache_free(struct kmem_cache *cachep, void *objp, struct array_cache *ac = cpu_cache_get(cachep); check_irq_off(); - if (unlikely(slab_want_init_on_free(cachep))) - memset(objp, 0, cachep->object_size); kmemleak_free_recursive(objp, cachep->flags); objp = cache_free_debugcheck(cachep, objp, caller); @@ -3484,7 +3514,7 @@ void ___cache_free(struct kmem_cache *cachep, void *objp, */ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) { - void *ret = slab_alloc(cachep, flags, _RET_IP_); + void *ret = slab_alloc(cachep, flags, cachep->object_size, _RET_IP_); trace_kmem_cache_alloc(_RET_IP_, ret, cachep->object_size, cachep->size, flags); @@ -3507,6 +3537,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) { size_t i; + struct kmem_cache *root_s = s; s = slab_pre_alloc_hook(s, flags); if (!s) @@ -3516,7 +3547,13 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, local_irq_disable(); for (i = 0; i < size; i++) { - void *objp = __do_cache_alloc(s, flags); + /* + * 5.4 note: passing in original cachep to avoid problems with + * memcg accounting. Making KFENCE properly work with memcgs on + * older kernels is not worth the effort. + */ + void *objp = kfence_alloc(root_s, s->object_size, flags) ?: + __do_cache_alloc(s, flags); if (unlikely(!objp)) goto error; @@ -3549,7 +3586,7 @@ kmem_cache_alloc_trace(struct kmem_cache *cachep, gfp_t flags, size_t size) { void *ret; - ret = slab_alloc(cachep, flags, _RET_IP_); + ret = slab_alloc(cachep, flags, size, _RET_IP_); ret = kasan_kmalloc(cachep, ret, size, flags); trace_kmalloc(_RET_IP_, ret, @@ -3575,7 +3612,7 @@ EXPORT_SYMBOL(kmem_cache_alloc_trace); */ void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid) { - void *ret = slab_alloc_node(cachep, flags, nodeid, _RET_IP_); + void *ret = slab_alloc_node(cachep, flags, nodeid, cachep->object_size, _RET_IP_); trace_kmem_cache_alloc_node(_RET_IP_, ret, cachep->object_size, cachep->size, @@ -3593,7 +3630,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *cachep, { void *ret; - ret = slab_alloc_node(cachep, flags, nodeid, _RET_IP_); + ret = slab_alloc_node(cachep, flags, nodeid, size, _RET_IP_); ret = kasan_kmalloc(cachep, ret, size, flags); trace_kmalloc_node(_RET_IP_, ret, @@ -3654,7 +3691,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, cachep = kmalloc_slab(size, flags); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; - ret = slab_alloc(cachep, flags, caller); + ret = slab_alloc(cachep, flags, size, caller); ret = kasan_kmalloc(cachep, ret, size, flags); trace_kmalloc(caller, ret, @@ -4203,7 +4240,10 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page, BUG_ON(objnr >= cachep->num); /* Find offset within object. */ - offset = ptr - index_to_obj(cachep, page, objnr) - obj_offset(cachep); + if (is_kfence_address(ptr)) + offset = ptr - kfence_object_start(ptr); + else + offset = ptr - index_to_obj(cachep, page, objnr) - obj_offset(cachep); /* Allow address range falling entirely within usercopy region. */ if (offset >= cachep->useroffset && diff --git a/mm/slab_common.c b/mm/slab_common.c index ca398f6bfa48..f024c2067aa0 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -593,6 +594,7 @@ static void slab_caches_to_rcu_destroy_workfn(struct work_struct *work) rcu_barrier(); list_for_each_entry_safe(s, s2, &to_destroy, list) { + kfence_shutdown_cache(s); #ifdef SLAB_SUPPORTS_SYSFS sysfs_slab_release(s); #else @@ -619,6 +621,7 @@ static int shutdown_cache(struct kmem_cache *s) list_add_tail(&s->list, &slab_caches_to_rcu_destroy); schedule_work(&slab_caches_to_rcu_destroy_work); } else { + kfence_shutdown_cache(s); #ifdef SLAB_SUPPORTS_SYSFS sysfs_slab_unlink(s); sysfs_slab_release(s); @@ -1870,7 +1873,7 @@ size_t ksize(const void *objp) if (unlikely(objp == ZERO_SIZE_PTR) || !__kasan_check_read(objp, 1)) return 0; - size = __ksize(objp); + size = kfence_ksize(objp) ?: __ksize(objp); /* * We assume that ksize callers could use whole allocated area, * so we need to unpoison this area. diff --git a/mm/slub.c b/mm/slub.c index ff869e0e2369..638736789987 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1486,6 +1487,11 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s, void *old_tail = *tail ? *tail : *head; int rsize; + if (is_kfence_address(next)) { + slab_free_hook(s, next); + return true; + } + /* Head and tail of the reconstructed freelist */ *head = NULL; *tail = NULL; @@ -2770,16 +2776,27 @@ static __always_inline void maybe_wipe_obj_freeptr(struct kmem_cache *s, * Otherwise we can simply pick the next object from the lockless free list. */ static __always_inline void *slab_alloc_node(struct kmem_cache *s, - gfp_t gfpflags, int node, unsigned long addr) + gfp_t gfpflags, int node, unsigned long addr, size_t orig_size) { void *object; struct kmem_cache_cpu *c; struct page *page; unsigned long tid; + struct kmem_cache *root_s = s; s = slab_pre_alloc_hook(s, gfpflags); if (!s) return NULL; + + /* + * 5.4 note: passing in original cachep to avoid problems with memcg + * accounting. Making KFENCE properly work with memcgs on older kernels + * is not worth the effort. + */ + object = kfence_alloc(root_s, orig_size, gfpflags); + if (unlikely(object)) + goto out; + redo: /* * Must read kmem_cache cpu data via this cpu ptr. Preemption is @@ -2853,20 +2870,21 @@ redo: if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object) memset(object, 0, s->object_size); +out: slab_post_alloc_hook(s, gfpflags, 1, &object); return object; } static __always_inline void *slab_alloc(struct kmem_cache *s, - gfp_t gfpflags, unsigned long addr) + gfp_t gfpflags, unsigned long addr, size_t orig_size) { - return slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr); + return slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr, orig_size); } void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags) { - void *ret = slab_alloc(s, gfpflags, _RET_IP_); + void *ret = slab_alloc(s, gfpflags, _RET_IP_, s->object_size); trace_kmem_cache_alloc(_RET_IP_, ret, s->object_size, s->size, gfpflags); @@ -2878,7 +2896,7 @@ EXPORT_SYMBOL(kmem_cache_alloc); #ifdef CONFIG_TRACING void *kmem_cache_alloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size) { - void *ret = slab_alloc(s, gfpflags, _RET_IP_); + void *ret = slab_alloc(s, gfpflags, _RET_IP_, size); trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags); ret = kasan_kmalloc(s, ret, size, gfpflags); return ret; @@ -2889,7 +2907,7 @@ EXPORT_SYMBOL(kmem_cache_alloc_trace); #ifdef CONFIG_NUMA void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node) { - void *ret = slab_alloc_node(s, gfpflags, node, _RET_IP_); + void *ret = slab_alloc_node(s, gfpflags, node, _RET_IP_, s->object_size); trace_kmem_cache_alloc_node(_RET_IP_, ret, s->object_size, s->size, gfpflags, node); @@ -2903,7 +2921,7 @@ void *kmem_cache_alloc_node_trace(struct kmem_cache *s, gfp_t gfpflags, int node, size_t size) { - void *ret = slab_alloc_node(s, gfpflags, node, _RET_IP_); + void *ret = slab_alloc_node(s, gfpflags, node, _RET_IP_, size); trace_kmalloc_node(_RET_IP_, ret, size, s->size, gfpflags, node); @@ -2937,6 +2955,9 @@ static void __slab_free(struct kmem_cache *s, struct page *page, stat(s, FREE_SLOWPATH); + if (kfence_free(head)) + return; + if (kmem_cache_debug(s) && !free_debug_processing(s, page, head, tail, cnt, addr)) return; @@ -3178,6 +3199,13 @@ int build_detached_freelist(struct kmem_cache *s, size_t size, df->s = cache_from_obj(s, object); /* Support for memcg */ } + if (is_kfence_address(object)) { + slab_free_hook(df->s, object); + __kfence_free(object); + p[size] = NULL; /* mark object processed */ + return size; + } + /* Start new detached freelist */ df->page = page; set_freepointer(df->s, object, NULL); @@ -3237,6 +3265,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, { struct kmem_cache_cpu *c; int i; + struct kmem_cache *root_s = s; /* memcg and kmem_cache debug support */ s = slab_pre_alloc_hook(s, flags); @@ -3251,8 +3280,19 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, c = this_cpu_ptr(s->cpu_slab); for (i = 0; i < size; i++) { - void *object = c->freelist; + /* + * 5.4 note: passing in original cachep to avoid problems with memcg + * accounting. Making KFENCE properly work with memcgs on older kernels + * is not worth the effort. + */ + void *object = kfence_alloc(root_s, s->object_size, flags); + if (unlikely(object)) { + p[i] = object; + continue; + } + + object = c->freelist; if (unlikely(!object)) { /* * We may have removed an object from c->freelist using @@ -3911,7 +3951,7 @@ void *__kmalloc(size_t size, gfp_t flags) if (unlikely(ZERO_OR_NULL_PTR(s))) return s; - ret = slab_alloc(s, flags, _RET_IP_); + ret = slab_alloc(s, flags, _RET_IP_, size); trace_kmalloc(_RET_IP_, ret, size, s->size, flags); @@ -3959,7 +3999,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node) if (unlikely(ZERO_OR_NULL_PTR(s))) return s; - ret = slab_alloc_node(s, flags, node, _RET_IP_); + ret = slab_alloc_node(s, flags, node, _RET_IP_, size); trace_kmalloc_node(_RET_IP_, ret, size, s->size, flags, node); @@ -3985,6 +4025,7 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page, struct kmem_cache *s; unsigned int offset; size_t object_size; + bool is_kfence = is_kfence_address(ptr); ptr = kasan_reset_tag(ptr); @@ -3997,10 +4038,13 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page, to_user, 0, n); /* Find offset within object. */ - offset = (ptr - page_address(page)) % s->size; + if (is_kfence) + offset = ptr - kfence_object_start(ptr); + else + offset = (ptr - page_address(page)) % s->size; /* Adjust for redzone and reject if within the redzone. */ - if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) { + if (!is_kfence && kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) { if (offset < s->red_left_pad) usercopy_abort("SLUB object in left red zone", s->name, to_user, offset, n); @@ -4447,7 +4491,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller) if (unlikely(ZERO_OR_NULL_PTR(s))) return s; - ret = slab_alloc(s, gfpflags, caller); + ret = slab_alloc(s, gfpflags, caller, size); /* Honor the call site pointer we received. */ trace_kmalloc(caller, ret, size, s->size, gfpflags); @@ -4478,7 +4522,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags, if (unlikely(ZERO_OR_NULL_PTR(s))) return s; - ret = slab_alloc_node(s, gfpflags, node, caller); + ret = slab_alloc_node(s, gfpflags, node, caller, size); /* Honor the call site pointer we received. */ trace_kmalloc_node(caller, ret, size, s->size, gfpflags, node); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 00438505c175..ec4dee64357b 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -34,6 +34,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) const struct nf_br_ops *nf_ops; const unsigned char *dest; u16 vid = 0; +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + struct net_bridge_port *pdst; + br_get_dst_hook_t *get_dst_hook; +#endif if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) { kfree_skb(skb); @@ -82,6 +86,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) br_do_suppress_nd(skb, br, vid, NULL, msg); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + get_dst_hook = rcu_dereference(br_get_dst_hook); +#endif + dest = eth_hdr(skb)->h_dest; if (is_broadcast_ether_addr(dest)) { br_flood(br, skb, BR_PKT_BROADCAST, false, true); @@ -107,11 +115,24 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) br_multicast_flood(mdst, skb, false, true); else br_flood(br, skb, BR_PKT_MULTICAST, false, true); - } else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) { - br_forward(dst->dst, skb, false, true); } else { - br_flood(br, skb, BR_PKT_UNICAST, false, true); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + pdst = __br_get(get_dst_hook, NULL, NULL, &skb); + if (pdst) { + if (!skb) + goto out; + br_forward(pdst, skb, false, true); + } else +#endif + { + dst = br_fdb_find_rcu(br, dest, vid); + if (dst) + br_forward(dst->dst, skb, false, true); + else + br_flood(br, skb, BR_PKT_UNICAST, false, true); + } } + out: rcu_read_unlock(); return NETDEV_TX_OK; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ea0ddd513cc1..a5565b7dfb06 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -695,6 +695,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, br_set_gso_limits(br); kobject_uevent(&p->kobj, KOBJ_ADD); + call_netdevice_notifiers(NETDEV_BR_JOIN, dev); return 0; @@ -732,6 +733,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) if (!p || p->br != br) return -EINVAL; + call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); + /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index ace461e94830..02dbaf13f591 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -33,6 +33,11 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) /* Hook for external Multicast handler */ br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; EXPORT_SYMBOL(br_multicast_handle_hook); + +/* Hook for external forwarding logic */ +br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_get_dst_hook); + #endif int br_pass_frame_up(struct sk_buff *skb) @@ -94,6 +99,8 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb struct net_bridge *br; #ifdef CONFIG_HYFI_BRIDGE_HOOKS br_multicast_handle_hook_t *multicast_handle_hook; + struct net_bridge_port *pdst = NULL; + br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); #endif u16 vid = 0; @@ -168,7 +175,17 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb } break; case BR_PKT_UNICAST: - dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + pdst = __br_get(get_dst_hook, NULL, p, &skb); + if (pdst) { + if (!skb) + goto out; + } else +#endif + { + dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); + } + break; default: break; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index c7130fff57a0..023ecc1f4494 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -195,6 +195,9 @@ struct net_bridge_fdb_entry { struct net_bridge_fdb_key key; struct hlist_node fdb_node; + unsigned char is_local:1, + is_static:1; + unsigned long flags; unsigned char offloaded:1; diff --git a/net/core/dev.c b/net/core/dev.c index 8f9f92b8bd80..9b8bd2d89595 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1521,7 +1521,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) - N(PRE_CHANGEADDR) + N(PRE_CHANGEADDR) N(BR_JOIN) N(BR_LEAVE) } #undef N return "UNKNOWN_NETDEV_EVENT"; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0b21a9902e29..a7e5b5843539 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3359,19 +3359,7 @@ EXPORT_SYMBOL(skb_split); */ static int skb_prepare_for_shift(struct sk_buff *skb) { - int ret = 0; - - if (skb_cloned(skb)) { - /* Save and restore truesize: pskb_expand_head() may reallocate - * memory where ksize(kmalloc(S)) != ksize(kmalloc(S)), but we - * cannot change truesize at this point. - */ - unsigned int save_truesize = skb->truesize; - - ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); - skb->truesize = save_truesize; - } - return ret; + return skb_unclone_keeptruesize(skb, GFP_ATOMIC); } /** diff --git a/net/core/sock.c b/net/core/sock.c index cf74d31f61ff..1c9d99fe59cb 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -578,7 +578,7 @@ static int sock_bindtoindex_locked(struct sock *sk, int ifindex) /* Sorry... */ ret = -EPERM; - if (!ns_capable(net->user_ns, CAP_NET_RAW)) + if (sk->sk_bound_dev_if && !ns_capable(net->user_ns, CAP_NET_RAW)) goto out; ret = -EINVAL; @@ -1542,6 +1542,13 @@ static int sk_getsockopt(struct sock *sk, int level, int optname, v.val = sk->sk_bound_dev_if; break; + case SO_NETNS_COOKIE: + lv = sizeof(u64); + if (len != lv) + return -EINVAL; + v.val64 = atomic64_read(&sock_net(sk)->net_cookie); + break; + default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). diff --git a/net/ipv4/netfilter/ipt_NATTYPE.c b/net/ipv4/netfilter/ipt_NATTYPE.c index fae7cad3f89f..484c0c111e4a 100644 --- a/net/ipv4/netfilter/ipt_NATTYPE.c +++ b/net/ipv4/netfilter/ipt_NATTYPE.c @@ -53,8 +53,8 @@ static void nattype_nte_debug_print(const struct ipt_nattype *nte, &nte->range.min_addr.ip, ntohs(nte->range.min_proto.all), ntohs(nte->nat_port), &nte->dest_addr, ntohs(nte->dest_port)); - DEBUGP("Timeout[%lx], Expires[%lx]\n", nte->timeout_value, - nte->timeout.expires); + DEBUGP("Timeout[%lx], Expires[%lx], Current[%lx]\n", nte->timeout_value, + nte->timeout.expires, jiffies); } /* netfilter NATTYPE nattype_free() @@ -80,8 +80,9 @@ bool nattype_refresh_timer_impl(unsigned long nat_type, spin_unlock_bh(&nattype_lock); return false; } + DEBUGP("%s: timeout_value=%lx, jiffies=%lx", __func__, timeout_value, jiffies); if (del_timer(&nte->timeout)) { - nte->timeout.expires = timeout_value; + nte->timeout.expires = timeout_value + jiffies - nfct_time_stamp; add_timer(&nte->timeout); spin_unlock_bh(&nattype_lock); nattype_nte_debug_print(nte, "refresh"); @@ -293,7 +294,7 @@ static unsigned int nattype_nat(struct sk_buff *skb, * found the entry. */ if (!nattype_refresh_timer((unsigned long)nte, - jiffies + nte->timeout_value)) + nfct_time_stamp + nte->timeout_value)) break; /* netfilter @@ -326,6 +327,7 @@ static unsigned int nattype_forward(struct sk_buff *skb, const struct ipt_nattype_info *info = par->targinfo; u16 nat_port; enum ip_conntrack_dir dir; + unsigned long timeout_value; if (xt_hooknum(par) != NF_INET_POST_ROUTING) return XT_CONTINUE; @@ -358,7 +360,7 @@ static unsigned int nattype_forward(struct sk_buff *skb, * found the entry. */ if (!nattype_refresh_timer((unsigned long)nte, - ct->timeout)) + ct->timeout)) break; /* netfilter NATTYPE @@ -431,7 +433,8 @@ static unsigned int nattype_forward(struct sk_buff *skb, * entry as this one is timed out and will be removed * from the list shortly. */ - if (!nattype_refresh_timer((unsigned long)nte2, jiffies + nte2->timeout_value)) + timeout_value = nfct_time_stamp + nte2->timeout_value; + if (!nattype_refresh_timer((unsigned long)nte2, timeout_value)) break; /* netfilter NATTYPE diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index cd252f530a2e..9a6e8eb85bdb 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1492,7 +1492,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, return -ENOMEM; } - if (skb_unclone(skb, gfp)) + if (skb_unclone_keeptruesize(skb, gfp)) return -ENOMEM; /* Get a new skb... force flag on. */ @@ -1601,7 +1601,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) { u32 delta_truesize; - if (skb_unclone(skb, GFP_ATOMIC)) + if (skb_unclone_keeptruesize(skb, GFP_ATOMIC)) return -ENOMEM; delta_truesize = __pskb_trim_head(skb, len); @@ -3160,7 +3160,7 @@ start: cur_mss, GFP_ATOMIC)) return -ENOMEM; /* We'll try again later. */ } else { - if (skb_unclone(skb, GFP_ATOMIC)) + if (skb_unclone_keeptruesize(skb, GFP_ATOMIC)) return -ENOMEM; diff = tcp_skb_pcount(skb); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7b9ac231b654..318d0111455d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -208,6 +208,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_defrtr = 1, .accept_ra_from_local = 0, .accept_ra_min_hop_limit= 1, + .accept_ra_min_lft = 0, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1, @@ -263,6 +264,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_defrtr = 1, .accept_ra_from_local = 0, .accept_ra_min_hop_limit= 1, + .accept_ra_min_lft = 0, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1, @@ -2749,6 +2751,9 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) return; } + if (valid_lft != 0 && valid_lft < in6_dev->cnf.accept_ra_min_lft) + goto put; + /* * Two things going on here: * 1) Add routes for on-link prefixes @@ -5547,6 +5552,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; + array[DEVCONF_ACCEPT_RA_MIN_LFT] = cnf->accept_ra_min_lft; } static inline size_t inet6_ifla6_size(void) @@ -6714,6 +6720,13 @@ static const struct ctl_table addrconf_sysctl[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "accept_ra_min_lft", + .data = &ipv6_devconf.accept_ra_min_lft, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "accept_ra_pinfo", .data = &ipv6_devconf.accept_ra_pinfo, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f6b5340c5e11..653e5bc7f047 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1271,6 +1271,14 @@ static void ndisc_router_discovery(struct sk_buff *skb) goto skip_defrtr; } + lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); + if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_lft) { + ND_PRINTK(2, info, + "RA: router lifetime (%ds) is too short: %s\n", + lifetime, skb->dev->name); + goto skip_defrtr; + } + /* Do not accept RA with source-addr found on local machine unless * accept_ra_from_local is set to true. */ @@ -1283,8 +1291,6 @@ static void ndisc_router_discovery(struct sk_buff *skb) goto skip_defrtr; } - lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); - #ifdef CONFIG_IPV6_ROUTER_PREF pref = ra_msg->icmph.icmp6_router_pref; /* 10b is handled as if it were 00b (medium) */ @@ -1455,6 +1461,9 @@ skip_linkparms: if (ri->prefix_len == 0 && !in6_dev->cnf.accept_ra_defrtr) continue; + if (ri->lifetime != 0 && + ntohl(ri->lifetime) < in6_dev->cnf.accept_ra_min_lft) + continue; if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) continue; if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index e999b6d8da11..effa09102d3c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1853,8 +1853,12 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, /* Refresh the NAT type entry. */ #if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE) nattype_ref_timer = rcu_dereference(nattype_refresh_timer); - if (nattype_ref_timer) - nattype_ref_timer(ct->nattype_entry, ct->timeout); + if (nattype_ref_timer) { + if (nf_ct_is_confirmed(ct)) + nattype_ref_timer(ct->nattype_entry, ct->timeout); + else + nattype_ref_timer(ct->nattype_entry, extra_jiffies + nfct_time_stamp); + } #endif acct: diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include index 496d11c92c97..363c354e9562 100644 --- a/scripts/Kconfig.include +++ b/scripts/Kconfig.include @@ -51,3 +51,9 @@ gcc-version := $(shell,$(srctree)/scripts/gcc-version.sh $(CC)) cc-option-bit = $(if-success,$(CC) -Werror $(1) -E -x c /dev/null -o /dev/null,$(1)) m32-flag := $(cc-option-bit,-m32) m64-flag := $(cc-option-bit,-m64) + +# Get the assembler name, version, and error out if it is not supported. +as-info := $(shell,$(srctree)/scripts/as-version.sh $(CC) $(CLANG_FLAGS)) +$(error-if,$(success,test -z "$(as-info)"),Sorry$(comma) this assembler is not supported.) +as-name := $(shell,set -- $(as-info) && echo $1) +as-version := $(shell,set -- $(as-info) && echo $2) diff --git a/scripts/as-version.sh b/scripts/as-version.sh new file mode 100755 index 000000000000..1a21495e9ff0 --- /dev/null +++ b/scripts/as-version.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only +# +# Print the assembler name and its version in a 5 or 6-digit form. +# Also, perform the minimum version check. +# (If it is the integrated assembler, return 0 as the version, and +# skip the version check.) + +set -e + +# Convert the version string x.y.z to a canonical 5 or 6-digit form. +get_canonical_version() +{ + IFS=. + set -- $1 + + # If the 2nd or 3rd field is missing, fill it with a zero. + # + # The 4th field, if present, is ignored. + # This occurs in development snapshots as in 2.35.1.20201116 + echo $((10000 * $1 + 100 * ${2:-0} + ${3:-0})) +} + +# Clang fails to handle -Wa,--version unless -fno-integrated-as is given. +# We check -fintegrated-as, expecting it is explicitly passed in for the +# integrated assembler case. +check_integrated_as() +{ + while [ $# -gt 0 ]; do + if [ "$1" = -fintegrated-as ]; then + # For the integrated assembler, we do not check the + # version here. It is the same as the clang version, and + # it has been already checked by scripts/cc-version.sh. + echo LLVM 0 + exit 0 + fi + shift + done +} + +check_integrated_as "$@" + +orig_args="$@" + +# Get the first line of the --version output. +IFS=' +' +set -- $(LC_ALL=C "$@" -Wa,--version -c -x assembler /dev/null -o /dev/null 2>/dev/null) + +# Split the line on spaces. +IFS=' ' +set -- $1 + +min_tool_version=$(dirname $0)/min-tool-version.sh + +if [ "$1" = GNU -a "$2" = assembler ]; then + shift $(($# - 1)) + version=$1 + min_version=$($min_tool_version binutils) + name=GNU +else + echo "$orig_args: unknown assembler invoked" >&2 + exit 1 +fi + +# Some distributions append a package release number, as in 2.34-4.fc32 +# Trim the hyphen and any characters that follow. +version=${version%-*} + +cversion=$(get_canonical_version $version) +min_cversion=$(get_canonical_version $min_version) + +if [ "$cversion" -lt "$min_cversion" ]; then + echo >&2 "***" + echo >&2 "*** Assembler is too old." + echo >&2 "*** Your $name assembler version: $version" + echo >&2 "*** Minimum $name assembler version: $min_version" + echo >&2 "***" + exit 1 +fi + +echo $name $cversion diff --git a/scripts/dummy-tools/gcc b/scripts/dummy-tools/gcc new file mode 100755 index 000000000000..9f5e943e7491 --- /dev/null +++ b/scripts/dummy-tools/gcc @@ -0,0 +1,98 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only +# +# Staring v4.18, Kconfig evaluates compiler capabilities, and hides CONFIG +# options your compiler does not support. This works well if you configure and +# build the kernel on the same host machine. +# +# It is inconvenient if you prepare the .config that is carried to a different +# build environment (typically this happens when you package the kernel for +# distros) because using a different compiler potentially produces different +# CONFIG options than the real build environment. So, you probably want to make +# as many options visible as possible. In other words, you need to create a +# super-set of CONFIG options that cover any build environment. If some of the +# CONFIG options turned out to be unsupported on the build machine, they are +# automatically disabled by the nature of Kconfig. +# +# However, it is not feasible to get a full-featured compiler for every arch. +# Hence these dummy toolchains to make all compiler tests pass. +# +# Usage: +# +# From the top directory of the source tree, run +# +# $ make CROSS_COMPILE=scripts/dummy-tools/ oldconfig +# +# Most of compiler features are tested by cc-option, which simply checks the +# exit code of $(CC). This script does nothing and just exits with 0 in most +# cases. So, $(cc-option, ...) is evaluated as 'y'. +# +# This scripts caters to more checks; handle --version and pre-process __GNUC__ +# etc. to pretend to be GCC, and also do right things to satisfy some scripts. + +# Check if the first parameter appears in the rest. Succeeds if found. +# This helper is useful if a particular option was passed to this script. +# Typically used like this: +# arg_contain "$@" +arg_contain () +{ + search="$1" + shift + + while [ $# -gt 0 ] + do + if [ "$search" = "$1" ]; then + return 0 + fi + shift + done + + return 1 +} + +# To set CONFIG_CC_IS_GCC=y +if arg_contain --version "$@"; then + echo "gcc (scripts/dummy-tools/gcc)" + exit 0 +fi + +if arg_contain -E "$@"; then + # For scripts/gcc-version.sh; This emulates GCC 20.0.0 + if arg_contain - "$@"; then + sed 's/^__GNUC__$/20/; s/^__GNUC_MINOR__$/0/; s/^__GNUC_PATCHLEVEL__$/0/' + exit 0 + else + echo "no input files" >&2 + exit 1 + fi +fi + +# To set CONFIG_AS_IS_GNU +if arg_contain -Wa,--version "$@"; then + echo "GNU assembler (scripts/dummy-tools) 2.50" + exit 0 +fi + +if arg_contain -S "$@"; then + # For scripts/gcc-x86-*-has-stack-protector.sh + if arg_contain -fstack-protector "$@"; then + echo "%gs" + exit 0 + fi +fi + +# To set GCC_PLUGINS +if arg_contain -print-file-name=plugin "$@"; then + plugin_dir=$(mktemp -d) + + mkdir -p $plugin_dir/include + touch $plugin_dir/include/plugin-version.h + + echo $plugin_dir + exit 0 +fi + +# inverted return value +if arg_contain -D__SIZEOF_INT128__=0 "$@"; then + exit 1 +fi diff --git a/scripts/dummy-tools/ld b/scripts/dummy-tools/ld new file mode 100755 index 000000000000..f68233050405 --- /dev/null +++ b/scripts/dummy-tools/ld @@ -0,0 +1,30 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only + +# Dummy script that always succeeds. + +# Check if the first parameter appears in the rest. Succeeds if found. +# This helper is useful if a particular option was passed to this script. +# Typically used like this: +# arg_contain "$@" +arg_contain () +{ + search="$1" + shift + + while [ $# -gt 0 ] + do + if [ "$search" = "$1" ]; then + return 0 + fi + shift + done + + return 1 +} + +if arg_contain --version "$@" || arg_contain -v "$@"; then + progname=$(basename $0) + echo "GNU $progname (scripts/dummy-tools/$progname) 2.50" + exit 0 +fi diff --git a/scripts/dummy-tools/nm b/scripts/dummy-tools/nm new file mode 120000 index 000000000000..c0648b38dd42 --- /dev/null +++ b/scripts/dummy-tools/nm @@ -0,0 +1 @@ +ld \ No newline at end of file diff --git a/scripts/dummy-tools/objcopy b/scripts/dummy-tools/objcopy new file mode 120000 index 000000000000..c0648b38dd42 --- /dev/null +++ b/scripts/dummy-tools/objcopy @@ -0,0 +1 @@ +ld \ No newline at end of file diff --git a/techpack/audio/asoc/codecs/wcd938x/wcd938x.c b/techpack/audio/asoc/codecs/wcd938x/wcd938x.c index 5f33ec26f4b8..d124b31b8e9c 100644 --- a/techpack/audio/asoc/codecs/wcd938x/wcd938x.c +++ b/techpack/audio/asoc/codecs/wcd938x/wcd938x.c @@ -3381,35 +3381,35 @@ static const struct snd_soc_dapm_widget wcd938x_dapm_widgets[] = { SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, ADC4, 0, adc4_switch, ARRAY_SIZE(adc4_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, DMIC1, + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, DMIC0, 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, DMIC2, + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, DMIC1, 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, DMIC3, + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, DMIC2, 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, DMIC4, + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, DMIC3, 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, DMIC5, + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, DMIC4, 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, DMIC6, + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, DMIC5, 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, DMIC7, + SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, DMIC6, 0, dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, DMIC8, + SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, DMIC7, 0, dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd938x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), diff --git a/techpack/audio/asoc/lahaina-port-config.h b/techpack/audio/asoc/lahaina-port-config.h index 62cdd512be20..12f89b0620b5 100644 --- a/techpack/audio/asoc/lahaina-port-config.h +++ b/techpack/audio/asoc/lahaina-port-config.h @@ -75,8 +75,8 @@ static struct port_params tx_frame_params_default[SWR_MSTR_PORT_LEN] = { /* TX UC1: TX1: 1ch, TX2: 2chs, TX3: 1ch(MBHC) */ static struct port_params tx_frame_params_shima[SWR_MSTR_PORT_LEN] = { {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX1 */ - {7, 5, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX2 */ - {7, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX3 */ + {7, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX2 */ + {7, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX3 */ }; /* 4.8 MHz clock */ diff --git a/techpack/audio/asoc/msm-audio-effects-q6-v2.c b/techpack/audio/asoc/msm-audio-effects-q6-v2.c index cb795f5bef45..4a7b4b32654e 100644 --- a/techpack/audio/asoc/msm-audio-effects-q6-v2.c +++ b/techpack/audio/asoc/msm-audio-effects-q6-v2.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -1091,7 +1092,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, u32 packed_data_size = 0; u8 *eq_config_data = NULL; u32 *updt_config_data = NULL; - int config_param_length; + int config_param_length, prev_config_param_length = 0; pr_debug("%s\n", __func__); if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) { @@ -1211,7 +1212,12 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, if (!eq_config_data) eq_config_data = kzalloc(config_param_length, GFP_KERNEL); - else + else if (config_param_length != prev_config_param_length) { + if (eq_config_data) + kfree(eq_config_data); + eq_config_data = kzalloc(config_param_length, + GFP_KERNEL); + } else memset(eq_config_data, 0, config_param_length); if (!eq_config_data) { pr_err("%s, EQ_CONFIG:memory alloc failed\n", @@ -1238,6 +1244,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, *updt_config_data++ = eq->per_band_cfg[idx].band_idx; } + prev_config_param_length = config_param_length; break; case EQ_BAND_INDEX: if (length != 1 || index_offset != 0) { @@ -1320,7 +1327,8 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, pr_debug("%s: did not send pp params\n", __func__); invalid_config: kfree(params); - kfree(eq_config_data); + if (eq_config_data) + kfree(eq_config_data); return rc; } EXPORT_SYMBOL(msm_audio_effects_popless_eq_handler); diff --git a/techpack/audio/asoc/msm-compress-q6-v2.c b/techpack/audio/asoc/msm-compress-q6-v2.c index 14f549310547..df48c414b05b 100644 --- a/techpack/audio/asoc/msm-compress-q6-v2.c +++ b/techpack/audio/asoc/msm-compress-q6-v2.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.​ */ @@ -4219,6 +4220,7 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, return -EINVAL; } + mutex_lock(&pdata->lock); cstream = pdata->cstream[fe_id]; if (cstream == NULL) { pr_err("%s cstream is null\n", __func__); @@ -4231,7 +4233,6 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - mutex_lock(&pdata->lock); if (prtd->audio_client == NULL) { pr_err("%s: audio_client is null\n", __func__); ret = -EINVAL; diff --git a/techpack/audio/asoc/msm-lsm-client.c b/techpack/audio/asoc/msm-lsm-client.c index bd9b73571263..7d69b97ca7c6 100644 --- a/techpack/audio/asoc/msm-lsm-client.c +++ b/techpack/audio/asoc/msm-lsm-client.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include @@ -42,6 +42,11 @@ #define LSM_IS_LAST_STAGE(client, stage_idx) \ (client->num_stages == (stage_idx + 1)) +struct lsm_char_dev { + /* Protects access to LSM client sessions and shared resources */ + struct mutex lock; +}; + static struct snd_pcm_hardware msm_pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -683,15 +688,47 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, struct lsm_params_info_v2 *p_info) { struct snd_pcm_runtime *runtime = substream->runtime; - struct lsm_priv *prtd = runtime->private_data; + struct lsm_priv *prtd = NULL; struct snd_soc_pcm_runtime *rtd = substream->private_data; int rc = 0; + struct lsm_char_dev *lsm_dev; + struct snd_soc_component *component = NULL; + + if (!rtd) { + pr_err("%s substream runtime or private_data not found\n", + __func__); + return -EINVAL; + } + component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + if (!component || !component->dev) { + pr_err("%s: invalid component\n", __func__); + return -EINVAL; + } + lsm_dev = (struct lsm_char_dev *) dev_get_drvdata(component->dev); + if (!lsm_dev) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&lsm_dev->lock); + if (!runtime) { + pr_err("%s: Invalid runtime", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } + prtd = runtime->private_data; + if (!prtd || !prtd->lsm_client) { + pr_err("%s: No LSM session active\n", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } if (p_info->param_type == LSM_MULTI_SND_MODEL_CONFIDENCE_LEVELS) { if (p_info->param_size > MAX_KEYWORDS_SUPPORTED) { dev_err(rtd->dev, "%s: invalid number of snd_model keywords %d, the max is %d\n", __func__, p_info->param_size, MAX_KEYWORDS_SUPPORTED); + mutex_unlock(&lsm_dev->lock); return -EINVAL; } @@ -702,6 +739,7 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: get_conf_levels failed for snd_model %d, err = %d\n", __func__, p_info->model_id, rc); + mutex_unlock(&lsm_dev->lock); return rc; } @@ -722,6 +760,7 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: invalid confidence levels %d\n", __func__, p_info->param_size); + mutex_unlock(&lsm_dev->lock); return -EINVAL; } @@ -733,6 +772,7 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: get_conf_levels failed, err = %d\n", __func__, rc); + mutex_unlock(&lsm_dev->lock); return rc; } @@ -749,6 +789,7 @@ static int msm_lsm_set_conf(struct snd_pcm_substream *substream, prtd->lsm_client->confidence_levels = NULL; } } + mutex_unlock(&lsm_dev->lock); return rc; } @@ -989,23 +1030,63 @@ static int msm_lsm_check_and_set_lab_controls(struct snd_pcm_substream *substrea u32 enable, struct lsm_params_info_v2 *p_info) { struct snd_pcm_runtime *runtime = substream->runtime; - struct lsm_priv *prtd = runtime->private_data; + struct lsm_priv *prtd = NULL; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct lsm_hw_params *out_hw_params = &prtd->lsm_client->out_hw_params; + struct lsm_hw_params *out_hw_params = NULL; + struct snd_soc_component *component = NULL; + struct lsm_char_dev *lsm_dev = NULL; u8 *chmap = NULL; u32 ch_idx; int rc = 0, stage_idx = p_info->stage_idx; + if (!rtd) { + pr_err("%s substream runtime or private_data not found\n", + __func__); + return -EINVAL; + } + component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + if (!component || !component->dev) { + pr_err("%s: invalid component\n", __func__); + return -EINVAL; + } + lsm_dev = (struct lsm_char_dev *) dev_get_drvdata(component->dev); + if (!lsm_dev) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&lsm_dev->lock); + if (!runtime) { + pr_err("%s: Invalid runtime", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } + prtd = runtime->private_data; + if (!prtd || !prtd->lsm_client) { + pr_err("%s: No LSM session active\n", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } + out_hw_params = &prtd->lsm_client->out_hw_params; + if (!out_hw_params) { + pr_err("%s: Invalid hw params\n", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } + if (prtd->lsm_client->stage_cfg[stage_idx].lab_enable == enable) { dev_dbg(rtd->dev, "%s: Lab for session %d, stage %d already %s\n", __func__, prtd->lsm_client->session, stage_idx, enable ? "enabled" : "disabled"); + mutex_unlock(&lsm_dev->lock); return rc; } chmap = kzalloc(out_hw_params->num_chs, GFP_KERNEL); - if (!chmap) + if (!chmap) { + mutex_unlock(&lsm_dev->lock); return -ENOMEM; + } rc = q6lsm_lab_control(prtd->lsm_client, enable, p_info); if (rc) { @@ -1046,6 +1127,7 @@ static int msm_lsm_check_and_set_lab_controls(struct snd_pcm_substream *substrea fail: kfree(chmap); + mutex_unlock(&lsm_dev->lock); return rc; } @@ -3106,9 +3188,11 @@ static int msm_lsm_close(struct snd_pcm_substream *substream) { unsigned long flags; struct snd_pcm_runtime *runtime = substream->runtime; - struct lsm_priv *prtd = runtime->private_data; + struct lsm_priv *prtd = NULL; struct snd_soc_pcm_runtime *rtd; struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + struct lsm_char_dev *lsm_dev; + struct snd_soc_component *component = NULL; int ret = 0; int be_id = 0; int fe_id = 0; @@ -3117,12 +3201,29 @@ static int msm_lsm_close(struct snd_pcm_substream *substream) pr_err("%s: Invalid private_data", __func__); return -EINVAL; } - if (!prtd || !prtd->lsm_client) { - pr_err("%s: No LSM session active\n", __func__); + if (!component || !component->dev) { + pr_err("%s: Invalid component\n", __func__); return -EINVAL; } rtd = substream->private_data; + lsm_dev = (struct lsm_char_dev *) dev_get_drvdata(component->dev); + if (!lsm_dev) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&lsm_dev->lock); + if (!runtime) { + pr_err("%s: Invalid runtime", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } + prtd = runtime->private_data; + if (!prtd || !prtd->lsm_client) { + pr_err("%s: No LSM session active\n", __func__); + mutex_unlock(&lsm_dev->lock); + return -EINVAL; + } dev_dbg(rtd->dev, "%s\n", __func__); if (prtd->lsm_client->started) { if (prtd->lsm_client->lab_enable) { @@ -3232,6 +3333,7 @@ static int msm_lsm_close(struct snd_pcm_substream *substream) mutex_destroy(&prtd->lsm_api_lock); kfree(prtd); runtime->private_data = NULL; + mutex_unlock(&lsm_dev->lock); return 0; } @@ -3240,21 +3342,36 @@ static int msm_lsm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; - struct lsm_priv *prtd = runtime->private_data; + struct lsm_priv *prtd = NULL; struct lsm_hw_params *out_hw_params = NULL; struct lsm_hw_params *in_hw_params = NULL; struct snd_soc_pcm_runtime *rtd; + struct lsm_char_dev *lsm_dev = NULL; + struct snd_soc_component *component = NULL; if (!substream->private_data) { pr_err("%s: Invalid private_data", __func__); return -EINVAL; } rtd = substream->private_data; + component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + if (!component || !component->dev) { + pr_err("%s: Invalid component\n", __func__); + return -EINVAL; + } + lsm_dev = (struct lsm_char_dev *) dev_get_drvdata(component->dev); + if (!lsm_dev) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&lsm_dev->lock); + prtd = runtime->private_data; if (!prtd || !params) { dev_err(rtd->dev, "%s: invalid params prtd %pK params %pK", __func__, prtd, params); + mutex_unlock(&lsm_dev->lock); return -EINVAL; } in_hw_params = &prtd->lsm_client->in_hw_params; @@ -3269,6 +3386,7 @@ static int msm_lsm_hw_params(struct snd_pcm_substream *substream, "%s: Invalid Params sample rate %d period count %d\n", __func__, out_hw_params->sample_rate, out_hw_params->period_count); + mutex_unlock(&lsm_dev->lock); return -EINVAL; } @@ -3279,6 +3397,7 @@ static int msm_lsm_hw_params(struct snd_pcm_substream *substream, } else { dev_err(rtd->dev, "%s: Invalid Format 0x%x\n", __func__, params_format(params)); + mutex_unlock(&lsm_dev->lock); return -EINVAL; } @@ -3299,6 +3418,7 @@ static int msm_lsm_hw_params(struct snd_pcm_substream *substream, */ memcpy(in_hw_params, out_hw_params, sizeof(struct lsm_hw_params)); + mutex_unlock(&lsm_dev->lock); return 0; } @@ -3629,6 +3749,14 @@ static struct snd_soc_component_driver msm_soc_component = { static int msm_lsm_probe(struct platform_device *pdev) { + struct lsm_char_dev *lsm_dev; + + lsm_dev = devm_kzalloc(&pdev->dev, sizeof(*lsm_dev), GFP_KERNEL); + if (!lsm_dev) + return -ENOMEM; + + mutex_init(&lsm_dev->lock); + dev_set_drvdata(&pdev->dev, lsm_dev); return snd_soc_register_component(&pdev->dev, &msm_soc_component, NULL, 0); @@ -3636,6 +3764,10 @@ static int msm_lsm_probe(struct platform_device *pdev) static int msm_lsm_remove(struct platform_device *pdev) { + struct lsm_char_dev *lsm_dev; + lsm_dev = dev_get_drvdata(&pdev->dev); + mutex_destroy(&lsm_dev->lock); + snd_soc_unregister_component(&pdev->dev); return 0; diff --git a/techpack/audio/asoc/msm-pcm-routing-v2.c b/techpack/audio/asoc/msm-pcm-routing-v2.c index c3b5f480993f..09b6ed61dd61 100644 --- a/techpack/audio/asoc/msm-pcm-routing-v2.c +++ b/techpack/audio/asoc/msm-pcm-routing-v2.c @@ -26945,6 +26945,10 @@ static const struct snd_kcontrol_new mmul17_mixer_controls[] = { MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_DOUBLE_EXT("INT3_MI2S_TX", SND_SOC_NOPM, MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer, @@ -41417,6 +41421,7 @@ static const struct snd_soc_dapm_route intercon_mi2s[] = { {"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia30 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia17 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia18 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia19 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia28 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, diff --git a/techpack/audio/asoc/msm_dailink.h b/techpack/audio/asoc/msm_dailink.h index c7353b9ae05d..ef588206d321 100644 --- a/techpack/audio/asoc/msm_dailink.h +++ b/techpack/audio/asoc/msm_dailink.h @@ -957,7 +957,8 @@ SND_SOC_DAILINK_DEFS(quat_mi2s_tx, SND_SOC_DAILINK_DEFS(quin_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.8")), - DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), + DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx"), + COMP_CODEC("acm8625s_codec", "acm8625s-hifi")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); SND_SOC_DAILINK_DEFS(quin_mi2s_tx, diff --git a/techpack/audio/dsp/q6adm.c b/techpack/audio/dsp/q6adm.c index cd876fde3f37..15a1d7830ebd 100644 --- a/techpack/audio/dsp/q6adm.c +++ b/techpack/audio/dsp/q6adm.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -3911,10 +3911,14 @@ void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate) pr_err("%s: unable to get channal map\n", __func__); goto fail_cmd; } - - for (i = 0; i < mfc_cfg.num_channels; i++) - mfc_cfg.channel_type[i] = + if (mfc_cfg.num_channels <= AUDPROC_MFC_OUT_CHANNELS_MAX) { + for (i = 0; i < mfc_cfg.num_channels; i++) + mfc_cfg.channel_type[i] = (uint16_t) open.dev_channel_mapping[i]; + } else { + pr_err("%s: size of num_channels is greater than channel type \n", __func__); + goto fail_cmd; + } atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); diff --git a/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index a3f090def566..28285a6eed57 100644 --- a/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -710,7 +710,10 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, return 0; } - if (release_hdr) + if (entry->hdr && entry == entry->hdr->proc_ctx) + entry->hdr->proc_ctx = NULL; + + if (entry->hdr && release_hdr) __ipa3_del_hdr(entry->hdr->id, false); /* move the offset entry to appropriate free list */ @@ -774,17 +777,19 @@ int __ipa3_del_hdr(u32 hdr_hdl, bool by_user) return 0; } + if (entry->proc_ctx && entry == entry->proc_ctx->hdr) + entry->proc_ctx->hdr = NULL; + if (entry->is_hdr_proc_ctx || entry->proc_ctx) { dma_unmap_single(ipa3_ctx->pdev, entry->phys_base, entry->hdr_len, DMA_TO_DEVICE); __ipa3_del_hdr_proc_ctx(entry->proc_ctx->id, false, false); - } else { - /* move the offset entry to appropriate free list */ - list_move(&entry->offset_entry->link, - &htbl->head_free_offset_list[entry->offset_entry->bin]); } + /* move the offset entry to appropriate free list */ + list_move(&entry->offset_entry->link, + &htbl->head_free_offset_list[entry->offset_entry->bin]); list_del(&entry->link); htbl->hdr_cnt--; entry->cookie = 0; diff --git a/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c b/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c index c2232d59b691..0db725a54626 100644 --- a/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c +++ b/techpack/dataipa/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -770,9 +771,9 @@ int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl) client->skip_clk_vote = params->skip_clk_vote; client->wlock = wakeup_source_register(NULL, client->name); if (!client->wlock) { - ipa_pm_deregister(*hdl); IPA_PM_ERR("IPA wakeup source register failed %s\n", client->name); + ipa_pm_deregister(*hdl); return -ENOMEM; } diff --git a/techpack/display/config/lahainadisp.conf b/techpack/display/config/lahainadisp.conf index d413be39445f..953a3ef338f8 100644 --- a/techpack/display/config/lahainadisp.conf +++ b/techpack/display/config/lahainadisp.conf @@ -4,7 +4,7 @@ export CONFIG_DRM_MSM_DP=y export CONFIG_DRM_MSM_DP_MST=y export CONFIG_SYNC_FILE=y export CONFIG_DRM_MSM_DSI=y -export CONFIG_DSI_PARSER=y +export CONFIG_DSI_PARSER=n export CONFIG_DRM_SDE_WB=y export CONFIG_DRM_MSM_REGISTER_LOGGING=y export CONFIG_QCOM_MDSS_PLL=y diff --git a/techpack/display/config/lahainadispconf.h b/techpack/display/config/lahainadispconf.h index e72e0a43f1ab..3a0210956e2a 100644 --- a/techpack/display/config/lahainadispconf.h +++ b/techpack/display/config/lahainadispconf.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #define CONFIG_DRM_MSM 1 @@ -9,7 +10,6 @@ #define CONFIG_DRM_MSM_SDE 1 #define CONFIG_SYNC_FILE 1 #define CONFIG_DRM_MSM_DSI 1 -#define CONFIG_DSI_PARSER 1 #define CONFIG_DRM_SDE_WB 1 #define CONFIG_DRM_MSM_REGISTER_LOGGING 1 #define CONFIG_DRM_SDE_EVTLOG_DEBUG 1 diff --git a/techpack/display/msm/dp/dp_display.c b/techpack/display/msm/dp/dp_display.c index ddafc11d5921..86d7873a7c26 100644 --- a/techpack/display/msm/dp/dp_display.c +++ b/techpack/display/msm/dp/dp_display.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ @@ -1184,6 +1184,7 @@ error_ctrl: static int dp_display_panel_ready(struct dp_display_private *dp) { int rc = 0; + bool skip_op = is_skip_required(&dp->dp_display); if (dp->dp_display.is_edp) { rc = dp->power->edp_panel_set_gpio(dp->power, DP_GPIO_EDP_VCC_EN, true); @@ -1201,9 +1202,9 @@ static int dp_display_panel_ready(struct dp_display_private *dp) } return -ETIMEDOUT; } + + dp->panel->init(dp->panel, skip_op); } - if (!dp->dp_display.cont_splash_enabled) - dp->panel->init(dp->panel); return 0; } @@ -1247,6 +1248,8 @@ static int dp_display_host_ready(struct dp_display_private *dp) dp->ctrl->abort(dp->ctrl, false); dp->aux->init(dp->aux, dp->parser->aux_cfg, skip_op); + dp->panel->init(dp->panel, skip_op); + dp_display_state_add(DP_STATE_READY); /* log this as it results from user action of cable connection */ DP_INFO("[OK]\n"); @@ -3918,6 +3921,7 @@ static int dp_display_get_display_type(struct dp_display *dp_display, const char **display_type) { struct dp_display_private *dp; + struct device_node *of_node; if (!dp_display || !display_type) { pr_err("invalid input\n"); @@ -3927,7 +3931,11 @@ static int dp_display_get_display_type(struct dp_display *dp_display, dp = container_of(dp_display, struct dp_display_private, dp_display); if (dp->parser) *display_type = dp->parser->display_type; - + else { + of_node = dp->pdev->dev.of_node; + *display_type = of_get_property(of_node, "qcom,display-type", + NULL); + } return 0; } diff --git a/techpack/display/msm/dp/dp_drm.c b/techpack/display/msm/dp/dp_drm.c index 6af98873cb38..ad90da674401 100644 --- a/techpack/display/msm/dp/dp_drm.c +++ b/techpack/display/msm/dp/dp_drm.c @@ -471,6 +471,7 @@ int dp_connector_get_info(struct drm_connector *connector, { struct dp_display *display = data; const char *display_type = NULL; + u32 conn_disp_type = SDE_CONNECTOR_PRIMARY; if (!info || !display || !display->drm_dev) { DP_ERR("invalid params\n"); @@ -480,11 +481,11 @@ int dp_connector_get_info(struct drm_connector *connector, info->intf_type = DRM_MODE_CONNECTOR_DisplayPort; display->get_display_type(display, &display_type); - if (display_type){ + if (display_type) { if (!strcmp(display_type, "primary")) - info->display_type = SDE_CONNECTOR_PRIMARY; + conn_disp_type = SDE_CONNECTOR_PRIMARY; else if (!strcmp(display_type, "secondary")) - info->display_type = SDE_CONNECTOR_SECONDARY; + conn_disp_type = SDE_CONNECTOR_SECONDARY; } info->num_of_h_tiles = 1; @@ -495,7 +496,8 @@ int dp_connector_get_info(struct drm_connector *connector, if (display && display->is_edp) { info->intf_type = DRM_MODE_CONNECTOR_eDP; - if(display->ext_hpd_en) + info->display_type = conn_disp_type; + if (display->ext_hpd_en) info->capabilities |= MSM_DISPLAY_CAP_HOT_PLUG; else info->is_connected = true; diff --git a/techpack/display/msm/dp/dp_panel.c b/techpack/display/msm/dp/dp_panel.c index 8fc1b13c3e32..a9f65f1aba8c 100644 --- a/techpack/display/msm/dp/dp_panel.c +++ b/techpack/display/msm/dp/dp_panel.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ @@ -2381,7 +2381,7 @@ error: return rc; } -static int dp_panel_init_panel_info(struct dp_panel *dp_panel) +static int dp_panel_init_panel_info(struct dp_panel *dp_panel, bool skip_op) { int rc = 0; struct dp_panel_private *panel; @@ -2393,6 +2393,9 @@ static int dp_panel_init_panel_info(struct dp_panel *dp_panel) goto end; } + if (skip_op) + goto end; + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); pinfo = &dp_panel->pinfo; diff --git a/techpack/display/msm/dp/dp_panel.h b/techpack/display/msm/dp/dp_panel.h index 3abb41297584..206b62dc32c7 100644 --- a/techpack/display/msm/dp/dp_panel.h +++ b/techpack/display/msm/dp/dp_panel.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ @@ -162,7 +162,7 @@ struct dp_panel { s64 fec_overhead_fp; - int (*init)(struct dp_panel *dp_panel); + int (*init)(struct dp_panel *dp_panel, bool skip_op); int (*deinit)(struct dp_panel *dp_panel, u32 flags); int (*hw_cfg)(struct dp_panel *dp_panel, bool enable); int (*read_sink_caps)(struct dp_panel *dp_panel, diff --git a/techpack/display/msm/dp/dp_parser.c b/techpack/display/msm/dp/dp_parser.c index ea5546ea476c..0a138bbe4a1d 100644 --- a/techpack/display/msm/dp/dp_parser.c +++ b/techpack/display/msm/dp/dp_parser.c @@ -178,13 +178,10 @@ static int dp_parser_misc(struct dp_parser *parser) &parser->pixel_base_off[i]); } - parser->display_type = of_get_property(of_node, "qcom,display-type", NULL); - if (!parser->display_type) { - if (parser->is_edp) - parser->display_type = "primary"; - else - parser->display_type = "secondary"; - } + parser->display_type = of_get_property(of_node, "qcom,display-type", + NULL); + if (!parser->display_type) + parser->display_type = "unknown"; parser->panel_notifier_support = of_property_read_bool(of_node, "qcom,panel-notifier-support"); diff --git a/techpack/display/msm/dsi/dsi_ctrl.c b/techpack/display/msm/dsi/dsi_ctrl.c index 278e3dcbe921..988b3e60a462 100644 --- a/techpack/display/msm/dsi/dsi_ctrl.c +++ b/techpack/display/msm/dsi/dsi_ctrl.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -229,10 +229,10 @@ static ssize_t debugfs_line_count_read(struct file *file, dsi_ctrl->cmd_trigger_frame); len += scnprintf((buf + len), max_len - len, "Command successful at line: %04x\n", - dsi_ctrl->cmd_success_line); + atomic_read(&dsi_ctrl->cmd_success_line)); len += scnprintf((buf + len), max_len - len, "Command successful at frame: %04x\n", - dsi_ctrl->cmd_success_frame); + atomic_read(&dsi_ctrl->cmd_success_frame)); mutex_unlock(&dsi_ctrl->ctrl_lock); @@ -2891,12 +2891,13 @@ static irqreturn_t dsi_ctrl_isr(int irq, void *ptr) if (dsi_ctrl->enable_cmd_dma_stats) { u32 reg = dsi_ctrl->hw.ops.log_line_count(&dsi_ctrl->hw, dsi_ctrl->cmd_mode); - dsi_ctrl->cmd_success_line = (reg & 0xFFFF); - dsi_ctrl->cmd_success_frame = ((reg >> 16) & 0xFFFF); + atomic_set(&dsi_ctrl->cmd_success_line, (reg & 0xFFFF)); + atomic_set(&dsi_ctrl->cmd_success_frame, ((reg >> 16) & 0xFFFF)); SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_CASE1, dsi_ctrl->cmd_success_line, dsi_ctrl->cmd_success_frame); } + atomic_set(&dsi_ctrl->dma_irq_trig, 1); dsi_ctrl_disable_status_interrupt(dsi_ctrl, DSI_SINT_CMD_MODE_DMA_DONE); diff --git a/techpack/display/msm/dsi/dsi_ctrl.h b/techpack/display/msm/dsi/dsi_ctrl.h index a3290750520b..944debaa28a4 100644 --- a/techpack/display/msm/dsi/dsi_ctrl.h +++ b/techpack/display/msm/dsi/dsi_ctrl.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ @@ -309,8 +310,8 @@ struct dsi_ctrl { bool cmd_mode; u32 cmd_trigger_line; u32 cmd_trigger_frame; - u32 cmd_success_line; - u32 cmd_success_frame; + atomic_t cmd_success_line; + atomic_t cmd_success_frame; }; /** diff --git a/techpack/display/msm/dsi/dsi_display.c b/techpack/display/msm/dsi/dsi_display.c index 506d24720a29..57b892d32810 100644 --- a/techpack/display/msm/dsi/dsi_display.c +++ b/techpack/display/msm/dsi/dsi_display.c @@ -6488,15 +6488,25 @@ static int dsi_display_init(struct dsi_display *display) if (rc) { DSI_ERR("[%s] failed to enable vregs, rc=%d\n", display->panel->name, rc); - return rc; + goto vreg_fail; } } rc = component_add(&pdev->dev, &dsi_display_comp_ops); - if (rc) + if (rc) { DSI_ERR("component add failed, rc=%d\n", rc); + goto comp_add_fail; + } DSI_DEBUG("component add success: %s\n", display->name); + return rc; + +comp_add_fail: + if (display->panel) + dsi_pwr_enable_regulator(&display->panel->power_info, false); +vreg_fail: + _dsi_display_dev_deinit(display); + end: return rc; } diff --git a/techpack/display/msm/msm_smmu.c b/techpack/display/msm/msm_smmu.c index 53f5f926560a..af9ba0ba87f5 100644 --- a/techpack/display/msm/msm_smmu.c +++ b/techpack/display/msm/msm_smmu.c @@ -1,4 +1,5 @@ /* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -211,6 +212,10 @@ static void msm_smmu_destroy(struct msm_mmu *mmu) { struct msm_smmu *smmu = to_msm_smmu(mmu); struct platform_device *pdev = to_platform_device(smmu->client_dev); + struct iommu_domain *domain = iommu_get_domain_for_dev(smmu->client_dev); + + if (domain) + iommu_set_fault_handler(domain, NULL, NULL); if (smmu->client_dev) platform_device_unregister(pdev); diff --git a/techpack/display/msm/sde/sde_kms.c b/techpack/display/msm/sde/sde_kms.c index 63e33a7481d5..ab090babd5e7 100644 --- a/techpack/display/msm/sde/sde_kms.c +++ b/techpack/display/msm/sde/sde_kms.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -1264,16 +1264,6 @@ static void _sde_kms_release_splash_resource(struct sde_kms *sde_kms, SDE_EVT32(DRMID(crtc), crtc->state->active, sde_kms->splash_data.num_splash_displays); - /*remove all votes if eDP displays are done with splash*/ - if (dp_display_get_num_of_boot_displays()) { - for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) - sde_power_data_bus_set_quota(phandle, i, - SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA, - phandle->ib_quota[i]); - pm_runtime_put_sync(sde_kms->dev->dev); - sde_kms->splash_data.num_splash_displays--; - } - for (i = 0; i < MAX_DSI_DISPLAYS; i++) { splash_display = &sde_kms->splash_data.splash_display[i]; if (splash_display->encoder && @@ -3405,7 +3395,7 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, struct msm_display_info info; struct drm_encoder *encoder = NULL; struct drm_crtc *crtc = NULL; - int i, rc = 0; + int i, rc = 0, splash_index = 0; struct drm_display_mode *drm_mode = NULL; struct drm_device *dev; struct msm_drm_private *priv; @@ -3443,7 +3433,7 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, DRM_INFO("cont_splash enabled in %d of %d display(s)\n", sde_kms->splash_data.num_splash_displays, - sde_kms->dsi_display_count); + sde_kms->dsi_display_count + sde_kms->dp_display_count); /* dsi */ for (i = 0; i < sde_kms->dsi_display_count; ++i) { @@ -3509,11 +3499,20 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, mutex_unlock(&dev->mode_config.mutex); return -EINVAL; } - mutex_unlock(&dev->mode_config.mutex); crtc->state->encoder_mask = (1 << drm_encoder_index(encoder)); + /* get supported modes in case of external bridge panels*/ + if (!dsi_display->panel->num_timing_nodes) { + connector->funcs->fill_modes(connector, + dev->mode_config.max_width, + dev->mode_config.max_height); + drm_mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + } + else + drm_mode = _sde_kms_get_splash_mode(sde_kms, connector, state); - drm_mode = _sde_kms_get_splash_mode(sde_kms, connector, state); + mutex_unlock(&dev->mode_config.mutex); if (!drm_mode) { SDE_ERROR("drm_mode not found; handoff_type:%d\n", sde_kms->splash_data.type); @@ -3570,6 +3569,19 @@ static int sde_kms_cont_splash_config(struct msm_kms *kms, break; } + splash_display = &sde_kms->splash_data.splash_display[splash_index]; + if (splash_display->cont_splash_enabled) { + priv = sde_kms->dev->dev_private; + encoder->crtc = priv->crtcs[splash_index]; + splash_display->encoder = encoder; + + SDE_DEBUG("dp-display:%d splash_index:%d crtc id[%d]:%d enc id[%d]:%d\n", + i, splash_index, encoder->crtc->index, encoder->crtc->base.id, + encoder->index, encoder->base.id); + + splash_index++; + } + mutex_lock(&dev->mode_config.mutex); drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { diff --git a/techpack/display/msm/sde/sde_wb.c b/techpack/display/msm/sde/sde_wb.c index dcb308fabf84..d6648ad7e2fc 100644 --- a/techpack/display/msm/sde/sde_wb.c +++ b/techpack/display/msm/sde/sde_wb.c @@ -204,6 +204,8 @@ int sde_wb_connector_set_modes(struct sde_wb_device *wb_dev, memset(&dispmode, 0, sizeof(dispmode)); ret = drm_mode_convert_umode(wb_dev->drm_dev, &dispmode, &modeinfo[i]); + /* null terminate the string */ + modeinfo[i].name[DRM_DISPLAY_MODE_LEN - 1] = '\0'; if (ret) { SDE_ERROR( "failed to convert mode %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x status:%d rc:%d\n", diff --git a/techpack/video/msm/vidc/msm_venc.c b/techpack/video/msm/vidc/msm_venc.c index 25a282e5aa4a..f95639e02465 100644 --- a/techpack/video/msm/vidc/msm_venc.c +++ b/techpack/video/msm/vidc/msm_venc.c @@ -3710,6 +3710,9 @@ int msm_venc_enable_hybrid_hp(struct msm_vidc_inst *inst) if (ctrl->val) return 0; + if (msm_vidc_get_fps(inst) <= 60) + return 0; + ctrl = get_ctrl(inst, V4L2_CID_MPEG_VIDC_VIDEO_HEVC_MAX_HIER_CODING_LAYER); layer = get_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER); diff --git a/techpack/video/msm/vidc/msm_vidc.c b/techpack/video/msm/vidc/msm_vidc.c index d848eb08976b..db0f6529abb8 100644 --- a/techpack/video/msm/vidc/msm_vidc.c +++ b/techpack/video/msm/vidc/msm_vidc.c @@ -1482,7 +1482,6 @@ static void close_helper(struct kref *kref) { struct msm_vidc_inst *inst = container_of(kref, struct msm_vidc_inst, kref); - msm_vidc_destroy(inst); } @@ -1497,19 +1496,19 @@ void *msm_vidc_open(int core_id, int session_type) session_type >= MSM_VIDC_MAX_DEVICES) { d_vpr_e("Invalid input, core_id = %d, session = %d\n", core_id, session_type); - goto err_invalid_core; + return NULL; } core = get_vidc_core(core_id); if (!core) { d_vpr_e("Failed to find core for core_id = %d\n", core_id); - goto err_invalid_core; + return NULL; } inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) { d_vpr_e("Failed to allocate memory\n"); rc = -ENOMEM; - goto err_invalid_core; + return NULL; } mutex_lock(&core->lock); rc = get_sid(&inst->sid, session_type); @@ -1609,14 +1608,15 @@ void *msm_vidc_open(int core_id, int session_type) s_vpr_e(inst->sid, "Failed to move video instance to init state\n"); kref_put(&inst->kref, close_helper); - inst = NULL; - goto err_invalid_core; + return NULL; } if (msm_comm_check_for_inst_overload(core)) { s_vpr_e(inst->sid, "Instance count reached Max limit, rejecting session"); - goto fail_init; + msm_comm_kill_session(inst); + kref_put(&inst->kref, close_helper); + return NULL; } msm_comm_scale_clocks_and_bus(inst, 1); @@ -1625,14 +1625,6 @@ void *msm_vidc_open(int core_id, int session_type) msm_vidc_debugfs_init_inst(inst, core->debugfs_root); return inst; -fail_init: - mutex_lock(&core->lock); - list_del(&inst->list); - mutex_unlock(&core->lock); - - v4l2_fh_del(&inst->event_handler); - v4l2_fh_exit(&inst->event_handler); - vb2_queue_release(&inst->bufq[INPUT_PORT].vb2_bufq); fail_bufq_output: vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq); fail_bufq_capture: @@ -1660,7 +1652,6 @@ err_invalid_sid: put_sid(inst->sid); kfree(inst); inst = NULL; -err_invalid_core: return inst; } EXPORT_SYMBOL(msm_vidc_open); diff --git a/tools/testing/selftests/kselftest_module.h b/tools/testing/selftests/kselftest_module.h index e8eafaf0941a..e2ea41de3f35 100644 --- a/tools/testing/selftests/kselftest_module.h +++ b/tools/testing/selftests/kselftest_module.h @@ -11,7 +11,8 @@ #define KSTM_MODULE_GLOBALS() \ static unsigned int total_tests __initdata; \ -static unsigned int failed_tests __initdata +static unsigned int failed_tests __initdata; \ +static unsigned int skipped_tests __initdata #define KSTM_CHECK_ZERO(x) do { \ total_tests++; \ @@ -21,11 +22,16 @@ static unsigned int failed_tests __initdata } \ } while (0) -static inline int kstm_report(unsigned int total_tests, unsigned int failed_tests) +static inline int kstm_report(unsigned int total_tests, unsigned int failed_tests, + unsigned int skipped_tests) { - if (failed_tests == 0) - pr_info("all %u tests passed\n", total_tests); - else + if (failed_tests == 0) { + if (skipped_tests) { + pr_info("skipped %u tests\n", skipped_tests); + pr_info("remaining %u tests passed\n", total_tests); + } else + pr_info("all %u tests passed\n", total_tests); + } else pr_warn("failed %u out of %u tests\n", failed_tests, total_tests); return failed_tests ? -EINVAL : 0; @@ -36,7 +42,7 @@ static int __init __module##_init(void) \ { \ pr_info("loaded.\n"); \ selftest(); \ - return kstm_report(total_tests, failed_tests); \ + return kstm_report(total_tests, failed_tests, skipped_tests); \ } \ static void __exit __module##_exit(void) \ { \ diff --git a/tools/testing/selftests/seccomp/seccomp_benchmark.c b/tools/testing/selftests/seccomp/seccomp_benchmark.c index 5838c8697ec3..fcc806585266 100644 --- a/tools/testing/selftests/seccomp/seccomp_benchmark.c +++ b/tools/testing/selftests/seccomp/seccomp_benchmark.c @@ -4,12 +4,16 @@ */ #define _GNU_SOURCE #include +#include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -18,9 +22,9 @@ unsigned long long timing(clockid_t clk_id, unsigned long long samples) { - pid_t pid, ret; - unsigned long long i; struct timespec start, finish; + unsigned long long i; + pid_t pid, ret; pid = getpid(); assert(clock_gettime(clk_id, &start) == 0); @@ -31,69 +35,208 @@ unsigned long long timing(clockid_t clk_id, unsigned long long samples) assert(clock_gettime(clk_id, &finish) == 0); i = finish.tv_sec - start.tv_sec; - i *= 1000000000; + i *= 1000000000ULL; i += finish.tv_nsec - start.tv_nsec; - printf("%lu.%09lu - %lu.%09lu = %llu\n", + printf("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n", finish.tv_sec, finish.tv_nsec, start.tv_sec, start.tv_nsec, - i); + i, (double)i / 1000000000.0); return i; } unsigned long long calibrate(void) { - unsigned long long i; + struct timespec start, finish; + unsigned long long i, samples, step = 9973; + pid_t pid, ret; + int seconds = 15; - printf("Calibrating reasonable sample size...\n"); + printf("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds); - for (i = 5; ; i++) { - unsigned long long samples = 1 << i; + samples = 0; + pid = getpid(); + assert(clock_gettime(CLOCK_MONOTONIC, &start) == 0); + do { + for (i = 0; i < step; i++) { + ret = syscall(__NR_getpid); + assert(pid == ret); + } + assert(clock_gettime(CLOCK_MONOTONIC, &finish) == 0); - /* Find something that takes more than 5 seconds to run. */ - if (timing(CLOCK_REALTIME, samples) / 1000000000ULL > 5) - return samples; + samples += step; + i = finish.tv_sec - start.tv_sec; + i *= 1000000000ULL; + i += finish.tv_nsec - start.tv_nsec; + } while (i < 1000000000ULL); + + return samples * seconds; +} + +bool approx(int i_one, int i_two) +{ + double one = i_one, one_bump = one * 0.01; + double two = i_two, two_bump = two * 0.01; + + one_bump = one + MAX(one_bump, 2.0); + two_bump = two + MAX(two_bump, 2.0); + + /* Equal to, or within 1% or 2 digits */ + if (one == two || + (one > two && one <= two_bump) || + (two > one && two <= one_bump)) + return true; + return false; +} + +bool le(int i_one, int i_two) +{ + if (i_one <= i_two) + return true; + return false; +} + +long compare(const char *name_one, const char *name_eval, const char *name_two, + unsigned long long one, bool (*eval)(int, int), unsigned long long two) +{ + bool good; + + printf("\t%s %s %s (%lld %s %lld): ", name_one, name_eval, name_two, + (long long)one, name_eval, (long long)two); + if (one > INT_MAX) { + printf("Miscalculation! Measurement went negative: %lld\n", (long long)one); + return 1; } + if (two > INT_MAX) { + printf("Miscalculation! Measurement went negative: %lld\n", (long long)two); + return 1; + } + + good = eval(one, two); + printf("%s\n", good ? "✔️" : "❌"); + + return good ? 0 : 1; } int main(int argc, char *argv[]) { + struct sock_filter bitmap_filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog bitmap_prog = { + .len = (unsigned short)ARRAY_SIZE(bitmap_filter), + .filter = bitmap_filter, + }; struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { .len = (unsigned short)ARRAY_SIZE(filter), .filter = filter, }; - long ret; - unsigned long long samples; - unsigned long long native, filtered; + + long ret, bits; + unsigned long long samples, calc; + unsigned long long native, filter1, filter2, bitmap1, bitmap2; + unsigned long long entry, per_filter1, per_filter2; + + printf("Current BPF sysctl settings:\n"); + system("sysctl net.core.bpf_jit_enable"); + system("sysctl net.core.bpf_jit_harden"); if (argc > 1) samples = strtoull(argv[1], NULL, 0); else samples = calibrate(); - printf("Benchmarking %llu samples...\n", samples); + printf("Benchmarking %llu syscalls...\n", samples); + /* Native call */ native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; printf("getpid native: %llu ns\n", native); ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); assert(ret == 0); + /* One filter resulting in a bitmap */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); + assert(ret == 0); + + bitmap1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1); + + /* Second filter resulting in a bitmap */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); + assert(ret == 0); + + bitmap2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2); + + /* Third filter, can no longer be converted to bitmap */ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); assert(ret == 0); - filtered = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; - printf("getpid RET_ALLOW: %llu ns\n", filtered); + filter1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1); - printf("Estimated seccomp overhead per syscall: %llu ns\n", - filtered - native); + /* Fourth filter, can not be converted to bitmap because of filter 3 */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog); + assert(ret == 0); - if (filtered == native) - printf("Trying running again with more samples.\n"); + filter2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2); + /* Estimations */ +#define ESTIMATE(fmt, var, what) do { \ + var = (what); \ + printf("Estimated " fmt ": %llu ns\n", var); \ + if (var > INT_MAX) \ + goto more_samples; \ + } while (0) + + ESTIMATE("total seccomp overhead for 1 bitmapped filter", calc, + bitmap1 - native); + ESTIMATE("total seccomp overhead for 2 bitmapped filters", calc, + bitmap2 - native); + ESTIMATE("total seccomp overhead for 3 full filters", calc, + filter1 - native); + ESTIMATE("total seccomp overhead for 4 full filters", calc, + filter2 - native); + ESTIMATE("seccomp entry overhead", entry, + bitmap1 - native - (bitmap2 - bitmap1)); + ESTIMATE("seccomp per-filter overhead (last 2 diff)", per_filter1, + filter2 - filter1); + ESTIMATE("seccomp per-filter overhead (filters / 4)", per_filter2, + (filter2 - native - entry) / 4); + + printf("Expectations:\n"); + ret |= compare("native", "≤", "1 bitmap", native, le, bitmap1); + bits = compare("native", "≤", "1 filter", native, le, filter1); + if (bits) + goto more_samples; + + ret |= compare("per-filter (last 2 diff)", "≈", "per-filter (filters / 4)", + per_filter1, approx, per_filter2); + + bits = compare("1 bitmapped", "≈", "2 bitmapped", + bitmap1 - native, approx, bitmap2 - native); + if (bits) { + printf("Skipping constant action bitmap expectations: they appear unsupported.\n"); + goto out; + } + + ret |= compare("entry", "≈", "1 bitmapped", entry, approx, bitmap1 - native); + ret |= compare("entry", "≈", "2 bitmapped", entry, approx, bitmap2 - native); + ret |= compare("native + entry + (per filter * 4)", "≈", "4 filters total", + entry + (per_filter1 * 4) + native, approx, filter2); + if (ret == 0) + goto out; + +more_samples: + printf("Saw unexpected benchmark result. Try running again with more samples?\n"); +out: return 0; } diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index a12eea3aff10..412e69e11620 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -3504,7 +3504,6 @@ TEST(seccomp_get_notif_sizes) /* * TODO: - * - add microbenchmarks * - expand NNP testing * - better arch-specific TRACE and TRAP handlers. * - endianness checking when appropriate @@ -3512,7 +3511,6 @@ TEST(seccomp_get_notif_sizes) * - arch value testing (x86 modes especially) * - verify that FILTER_FLAG_LOG filters generate log messages * - verify that RET_LOG generates log messages - * - ... */ TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/seccomp/settings b/tools/testing/selftests/seccomp/settings new file mode 100644 index 000000000000..6091b45d226b --- /dev/null +++ b/tools/testing/selftests/seccomp/settings @@ -0,0 +1 @@ +timeout=120