Compare commits

...

246 Commits

Author SHA1 Message Date
Linus Torvalds
5e5ea7f616 iommu/amd: fix SEV-TIO support reporting
Commit eeb934137d ("iommu/amd: Report SEV-TIO support") was confused
about the config options that expose amd_iommu_sev_tio_supported(), and
made the declaration (and alternative dummy function) conditional on the
CONFIG_AMD_IOMMU config option.

But the code is actually dependent on CONFIG_KVM_AMD_SEV, resulting in

   ERROR: modpost: "amd_iommu_sev_tio_supported" [drivers/crypto/ccp/ccp.ko] undefined!
   make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1

if you have the AMD iommu enabled, but don't enable KVM_AMD_SEV support.

Fix it by moving the declaration into the right #ifdef section in the
header file.

Fixes: eeb934137d ("iommu/amd: Report SEV-TIO support")
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Joerg Roedel <joerg.roedel@amd.com>
Cc: Vasant Hegde <vasant.hegde@amd.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2025-12-06 11:13:50 -08:00
Linus Torvalds
b0319c4642 Merge tag 'nfsd-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd updates from Chuck Lever:

 - Mike Snitzer's mechanism for disabling I/O caching introduced in
   v6.18 is extended to include using direct I/O. The goal is to further
   reduce the memory footprint consumed by NFS clients accessing large
   data sets via NFSD.

 - The NFSD community adopted a maintainer entry profile during this
   cycle. See

      Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst

 - Work continues on hardening NFSD's implementation of the pNFS block
   layout type. This type enables pNFS clients to directly access the
   underlying block devices that contain an exported file system,
   reducing server overhead and increasing data throughput.

 - The remaining patches are clean-ups and minor optimizations. Many
   thanks to the contributors, reviewers, testers, and bug reporters who
   participated during the v6.19 NFSD development cycle.

* tag 'nfsd-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (38 commits)
  NFSD: nfsd-io-modes: Separate lists
  NFSD: nfsd-io-modes: Wrap shell snippets in literal code blocks
  NFSD: Add toctree entry for NFSD IO modes docs
  NFSD: add Documentation/filesystems/nfs/nfsd-io-modes.rst
  NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
  NFSD: Make FILE_SYNC WRITEs comply with spec
  NFSD: Add trace point for SCSI fencing operation.
  NFSD: use correct reservation type in nfsd4_scsi_fence_client
  xdrgen: Don't generate unnecessary semicolon
  xdrgen: Fix union declarations
  NFSD: don't start nfsd if sv_permsocks is empty
  xdrgen: handle _XdrString in union encoder/decoder
  xdrgen: Fix the variable-length opaque field decoder template
  xdrgen: Make the xdrgen script location-independent
  xdrgen: Generalize/harden pathname construction
  lockd: don't allow locking on reexported NFSv2/3
  MAINTAINERS: add a nfsd blocklayout reviewer
  nfsd: Use MD5 library instead of crypto_shash
  nfsd: stop pretending that we cache the SEQUENCE reply.
  NFS: nfsd-maintainer-entry-profile: Inline function name prefixes
  ...
2025-12-06 10:57:02 -08:00
Linus Torvalds
1a68aefc71 Merge tag 'for-linus-6.19-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip
Pull xen updates from Juergen Gross:
 "This round it contains only three small cleanup patches"

* tag 'for-linus-6.19-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip:
  drivers/xen: use min() instead of min_t()
  drivers/xen/xenbus: Replace deprecated strcpy in xenbus_transaction_end
  drivers/xen/xenbus: Simplify return statement in join()
2025-12-06 10:49:19 -08:00
Linus Torvalds
249872f53d Merge tag 'tsm-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm
Pull PCIe Link Encryption and Device Authentication from Dan Williams:
 "New PCI infrastructure and one architecture implementation for PCIe
  link encryption establishment via platform firmware services.

  This work is the result of multiple vendors coming to consensus on
  some core infrastructure (thanks Alexey, Yilun, and Aneesh!), and
  three vendor implementations, although only one is included in this
  pull. The PCI core changes have an ack from Bjorn, the crypto/ccp/
  changes have an ack from Tom, and the iommu/amd/ changes have an ack
  from Joerg.

  PCIe link encryption is made possible by the soup of acronyms
  mentioned in the shortlog below. Link Integrity and Data Encryption
  (IDE) is a protocol for installing keys in the transmitter and
  receiver at each end of a link. That protocol is transported over Data
  Object Exchange (DOE) mailboxes using PCI configuration requests.

  The aspect that makes this a "platform firmware service" is that the
  key provisioning and protocol is coordinated through a Trusted
  Execution Envrionment (TEE) Security Manager (TSM). That is either
  firmware running in a coprocessor (AMD SEV-TIO), or quasi-hypervisor
  software (Intel TDX Connect / ARM CCA) running in a protected CPU
  mode.

  Now, the only reason to ask a TSM to run this protocol and install the
  keys rather than have a Linux driver do the same is so that later, a
  confidential VM can ask the TSM directly "can you certify this
  device?".

  That precludes host Linux from provisioning its own keys, because host
  Linux is outside the trust domain for the VM. It also turns out that
  all architectures, save for one, do not publish a mechanism for an OS
  to establish keys in the root port. So "TSM-established link
  encryption" is the only cross-architecture path for this capability
  for the foreseeable future.

  This unblocks the other arch implementations to follow in v6.20/v7.0,
  once they clear some other dependencies, and it unblocks the next
  phase of work to implement the end-to-end flow of confidential device
  assignment. The PCIe specification calls this end-to-end flow Trusted
  Execution Environment (TEE) Device Interface Security Protocol
  (TDISP).

  In the meantime, Linux gets a link encryption facility which has
  practical benefits along the same lines as memory encryption. It
  authenticates devices via certificates and may protect against
  interposer attacks trying to capture clear-text PCIe traffic.

  Summary:

   - Introduce the PCI/TSM core for the coordination of device
     authentication, link encryption and establishment (IDE), and later
     management of the device security operational states (TDISP).
     Notify the new TSM core layer of PCI device arrival and departure

   - Add a low level TSM driver for the link encryption establishment
     capabilities of the AMD SEV-TIO architecture

   - Add a library of helpers TSM drivers to use for IDE establishment
     and the DOE transport

   - Add skeleton support for 'bind' and 'guest_request' operations in
     support of TDISP"

* tag 'tsm-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm: (23 commits)
  crypto/ccp: Fix CONFIG_PCI=n build
  virt: Fix Kconfig warning when selecting TSM without VIRT_DRIVERS
  crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)
  iommu/amd: Report SEV-TIO support
  psp-sev: Assign numbers to all status codes and add new
  ccp: Make snp_reclaim_pages and __sev_do_cmd_locked public
  PCI/TSM: Add 'dsm' and 'bound' attributes for dependent functions
  PCI/TSM: Add pci_tsm_guest_req() for managing TDIs
  PCI/TSM: Add pci_tsm_bind() helper for instantiating TDIs
  PCI/IDE: Initialize an ID for all IDE streams
  PCI/IDE: Add Address Association Register setup for downstream MMIO
  resource: Introduce resource_assigned() for discerning active resources
  PCI/TSM: Drop stub for pci_tsm_doe_transfer()
  drivers/virt: Drop VIRT_DRIVERS build dependency
  PCI/TSM: Report active IDE streams
  PCI/IDE: Report available IDE streams
  PCI/IDE: Add IDE establishment helpers
  PCI: Establish document for PCI host bridge sysfs attributes
  PCI: Add PCIe Device 3 Extended Capability enumeration
  PCI/TSM: Establish Secure Sessions and Link Encryption
  ...
2025-12-06 10:15:41 -08:00
Linus Torvalds
fbff949679 Merge tag 'linux-watchdog-6.19-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:

 - Add watchdog support for:
     - Renesas WWDT
     - AST2700 platform
     - MediaTek MT8189 SoC
     - Loongson-2k0300 watchdog
     - Qualcomm Kaanapali watchdog
     - RK3506 compatible
     - Airoha AN7583 SoC

 - DT Schema conversions:
     - lantiq,wdt
     - TI OMAP
     - marvell,orion-wdt

 - Several other fixes and improvements

* tag 'linux-watchdog-6.19-rc1' of git://www.linux-watchdog.org/linux-watchdog: (30 commits)
  watchdog: starfive: Fix resource leak in probe error path
  dt-bindings: watchdog: airoha: Add support for Airoha AN7583 SoC
  dt-bindings: watchdog: lantiq,wdt: convert bindings to dtschema
  dt-bindings: watchdog: Add RK3506 compatible
  dt-bindings: watchdog: Document Qualcomm Kaanapali watchdog
  watchdog: wdat_wdt: Fix ACPI table leak in probe function
  watchdog: loongson1: Add Loongson-2k0300 watchdog support
  dt-bindings: watchdog: loongson,ls1x-wdt: Add ls2k0300-wdt compatible
  watchdog: loongson1: Drop CONFIG_OF
  watchdog: loongson1: Simplify ls1x_wdt_probe code
  watchdog: loongson1: Add missing MODULE_PARM_DESC
  watchdog/diag288: Fix module comment typos
  dt-bindings: watchdog: Support MediaTek MT8189 wdt
  dt-bindings: watchdog: mediatek,mtk-wdt: Add compatible for MT8189 SoC
  dt-bindings: mfd: rohm,bd96801-pmic: Correct timeout-sec length and reference watchdog schema
  dt-bindings: watchdog: Allow node names named 'pmic'
  dt-bindings: watchdog: Restrict timeout-sec to one number
  watchdog: renesas_wwdt: add driver
  dt-bindings: watchdog: Add Renesas WWDT
  dt-bindings: watchdog: Convert marvell,orion-wdt to DT schema
  ...
2025-12-06 10:00:49 -08:00
Linus Torvalds
973ec55764 Merge tag 'rpmsg-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux
Pull rpmsg updates from Bjorn Andersson:

 - Reduce code duplication related to channel removal, and invoke the
   removal in one case previously missing, both in the Glink driver

* tag 'rpmsg-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux:
  rpmsg: glink: remove duplicate code for rpmsg device remove
  rpmsg: glink: fix rpmsg device leak
2025-12-06 09:58:02 -08:00
Linus Torvalds
e637b37a52 Merge tag 'rproc-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux
Pull remoteproc updates from Bjorn Andersson:

 - Add support for the compute DSP in the Qualcomm SDM660 platform, and
   finally fix up the way MSM8974 audio DSP remoteproc driver manages
   its power rails

 - Replace the usage of of_reserved_mem_lookup() with
   of_reserved_mem_region_to_resource() to clean things up across most
   of the drivers

 - Perform a variety of housekeeping and cleanup work across iMX,
   Mediatek, and TI remoteproc drivers

* tag 'rproc-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux: (45 commits)
  remoteproc: qcom_q6v5_wcss: use optional reset for wcss_q6_bcr_reset
  remoteproc: qcom_q6v5_wcss: fix parsing of qcom,halt-regs
  remoteproc: qcom_wcnss: Fix NULL vs IS_ERR() bug in wcnss_alloc_memory_region()
  remoteproc: qcom: q6v5: Fix NULL vs IS_ERR() bug in q6v5_alloc_memory_region()
  remoteproc: qcom: pas: Fix a couple NULL vs IS_ERR() bugs
  remoteproc: qcom_q6v5_adsp: Fix a NULL vs IS_ERR() check in adsp_alloc_memory_region()
  remoteproc: imx_dsp_rproc: Fix NULL vs IS_ERR() bug in imx_dsp_rproc_add_carveout()
  remoteproc: st: Fix indexing of memory-regions
  remoteproc: qcom: pas: Add support for SDM660 CDSP
  dt-bindings: remoteproc: qcom: adsp: Add SDM660 CDSP compatible
  dt-bindings: remoteproc: qcom: adsp: Add missing constrains for SDM660 ADSP
  dt-bindings: remoteproc: qcom,sc8280xp-pas: Fix CDSP power desc
  remoteproc: omap: Remove redundant pm_runtime_mark_last_busy() calls
  remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"
  remoteproc: qcom_q6v5_pas: Use resource with CX PD for MSM8974
  dt-bindings: remoteproc: qcom,adsp: Make msm8974 use CX as power domain
  remoteproc: Use of_reserved_mem_region_* functions for "memory-region"
  remoteproc: imx_dsp_rproc: Simplify start/stop error handling
  remoteproc: imx_rproc: Remove enum imx_rproc_method
  remoteproc: imx_dsp_rproc: Simplify IMX_RPROC_RESET_CONTROLLER switch case
  ...
2025-12-06 09:55:38 -08:00
Linus Torvalds
eee654ca9a Merge tag 'landlock-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün:
 "This mainly fixes handling of disconnected directories and adds new
  tests"

* tag 'landlock-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  selftests/landlock: Add disconnected leafs and branch test suites
  selftests/landlock: Add tests for access through disconnected paths
  landlock: Improve variable scope
  landlock: Fix handling of disconnected directories
  selftests/landlock: Fix makefile header list
  landlock: Make docs in cred.h and domain.h visible
  landlock: Minor comments improvements
2025-12-06 09:52:41 -08:00
Linus Torvalds
10003ff8ce Merge tag 'turbostat-v2025.12.02' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
Pull turbostat updates from Len Brown:

 - Add LLC statistics columns:
	LLCkRPS = Last Level Cache Thousands of References Per Second
	LLC%hit = Last Level Cache Hit %

 - Recognize Wildcat Lake and Nova Lake platforms

 - Add MSR check for Android

 - Add APERF check for VMWARE

 - Add RAPL check for AWS

 - Minor fixes to turbostat (and x86_energy_perf_policy)

* tag 'turbostat-v2025.12.02' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux: (21 commits)
  tools/power turbostat: version 2025.12.02
  tools/power turbostat: Print wide names only for RAW 64-bit columns
  tools/power turbostat: Print percentages in 8-columns
  tools/power turbostat: Print "nan" for out of range percentages
  tools/power turbostat: Validate APERF access for VMWARE
  tools/power turbostat: Enhance perf probe
  tools/power turbostat: Validate RAPL MSRs for AWS Nitro Hypervisor
  tools/power x86_energy_perf_policy: Fix potential NULL pointer dereference
  tools/power x86_energy_perf_policy: Fix format string in error message
  tools/power x86_energy_perf_policy: Simplify Android MSR probe
  tools/power x86_energy_perf_policy: Add Android MSR device support
  tools/power turbostat: Add run-time MSR driver probe
  tools/power turbostat: Set per_cpu_msr_sum to NULL after free
  tools/power turbostat: Add LLC stats
  tools/power turbostat: Remove dead code
  tools/power turbostat: Refactor floating point printout code
  tools/power turbostat.8: Update example
  tools/power turbostat: Refactor added-counter value printing code
  tools/power turbostat: Refactor added column header printing
  tools/power turbostat: Add Wildcat Lake and Nova Lake support
  ...
2025-12-06 09:35:00 -08:00
Linus Torvalds
56a1a04dc9 Merge tag 'libnvdimm-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull nvdimm updates from Ira Weiny:
 "These are mainly bug fixes and code updates.

  There is a new feature to divide up memmap= carve outs and a fix
  caught in linux-next for that patch. Managing memmap memory on the fly
  for multiple VM's was proving difficult and Mike provided a driver
  which allows for the memory to be better manged.

  Summary:
   - Allow exposing RAM carveouts as NVDIMM DIMM devices
   - Prevent integer overflow in ramdax_get_config_data()
   - Replace use of system_wq with system_percpu_wq
   - Documentation: btt: Unwrap bit 31-30 nested table
   - tools/testing/nvdimm: Use per-DIMM device handle"

* tag 'libnvdimm-for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  nvdimm: Prevent integer overflow in ramdax_get_config_data()
  Documentation: btt: Unwrap bit 31-30 nested table
  nvdimm: replace use of system_wq with system_percpu_wq
  tools/testing/nvdimm: Use per-DIMM device handle
  nvdimm: allow exposing RAM carveouts as NVDIMM DIMM devices
2025-12-06 09:32:25 -08:00
Linus Torvalds
a7405aa92f Merge tag 'dma-mapping-6.19-2025-12-05' of git://git.kernel.org/pub/scm/linux/kernel/git/mszyprowski/linux
Pull dma-mapping updates from Marek Szyprowski:

 - More DMA mapping API refactoring to physical addresses as the primary
   interface instead of page+offset parameters.

   This time dma_map_ops callbacks are converted to physical addresses,
   what in turn results also in some simplification of architecture
   specific code (Leon Romanovsky and Jason Gunthorpe)

 - Clarify that dma_map_benchmark is not a kernel self-test, but
   standalone tool (Qinxin Xia)

* tag 'dma-mapping-6.19-2025-12-05' of git://git.kernel.org/pub/scm/linux/kernel/git/mszyprowski/linux:
  dma-mapping: remove unused map_page callback
  xen: swiotlb: Convert mapping routine to rely on physical address
  x86: Use physical address for DMA mapping
  sparc: Use physical address DMA mapping
  powerpc: Convert to physical address DMA mapping
  parisc: Convert DMA map_page to map_phys interface
  MIPS/jazzdma: Provide physical address directly
  alpha: Convert mapping routine to rely on physical address
  dma-mapping: remove unused mapping resource callbacks
  xen: swiotlb: Switch to physical address mapping callbacks
  ARM: dma-mapping: Switch to physical address mapping callbacks
  ARM: dma-mapping: Reduce struct page exposure in arch_sync_dma*()
  dma-mapping: convert dummy ops to physical address mapping
  dma-mapping: prepare dma_map_ops to conversion to physical address
  tools/dma: move dma_map_benchmark from selftests to tools/dma
2025-12-06 09:25:05 -08:00
Linus Torvalds
f468cf53c5 Merge tag 'bitmap-for-6.19' of github.com:/norov/linux
Pull bitmap updates from Yury Norov:

 - Runtime field_{get,prep}() (Geert)

 - Rust ID pool updates (Alice)

 - min_t() simplification (David)

 - __sw_hweightN kernel-doc fixes (Andy)

 - cpumask.h headers cleanup (Andy)

* tag 'bitmap-for-6.19' of github.com:/norov/linux: (32 commits)
  rust_binder: use bitmap for allocation of handles
  rust: id_pool: do not immediately acquire new ids
  rust: id_pool: do not supply starting capacity
  rust: id_pool: rename IdPool::new() to with_capacity()
  rust: bitmap: add BitmapVec::new_inline()
  rust: bitmap: add MAX_LEN and MAX_INLINE_LEN constants
  cpumask: Don't use "proxy" headers
  soc: renesas: Use bitfield helpers
  clk: renesas: Use bitfield helpers
  ALSA: usb-audio: Convert to common field_{get,prep}() helpers
  soc: renesas: rz-sysc: Convert to common field_get() helper
  pinctrl: ma35: Convert to common field_{get,prep}() helpers
  iio: mlx90614: Convert to common field_{get,prep}() helpers
  iio: dac: Convert to common field_prep() helper
  gpio: aspeed: Convert to common field_{get,prep}() helpers
  EDAC/ie31200: Convert to common field_get() helper
  crypto: qat - convert to common field_get() helper
  clk: at91: Convert to common field_{get,prep}() helpers
  bitfield: Add non-constant field_{prep,get}() helpers
  bitfield: Add less-checking __FIELD_{GET,PREP}()
  ...
2025-12-06 09:01:27 -08:00
Miguel Ojeda
309e49039f rust: sync: atomic: separate import "blocks"
Commit 14e9a18b07 ("rust: sync: atomic: Make Atomic*Ops pub(crate)")
added a `pub(crate)` import in the same "block" as the `pub` one,
without running `rustfmt`, which would sort them differently.

Instead of running `rustfmt` as-is, add a newline to keep the import
"blocks" with different visibilities separate.

Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2025-12-06 08:44:10 -08:00
Linus Torvalds
c84d574698 Merge tag 'modules-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux
Pull module updates from Daniel Gomez:
 "Rust module parameter support:

   - Add Rust module parameter support, enabling Rust kernel modules to
     declare and use module parameters. The rust_minimal sample module
     demonstrates this, and the rust null block driver will be the first
     to use it in the next cycle. This also adds the Rust module files
     under the modules subsystem as agreed between the Rust and modules
     maintainers.

  Hardening:

   - Add compile-time check for embedded NUL characters in MODULE_*()
     macros. This module metadata was once used (and maybe still) to
     bypass license enforcement (LWN article from 2003):

	https://lwn.net/Articles/82305/ [1]

  MAINTAINERS:

   - Add Aaron Tomlin as reviewer for the Modules subsystem"

* tag 'modules-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/modules/linux:
  MAINTAINERS: Add myself as reviewer for module support
  module: Add compile-time check for embedded NUL characters
  media: radio: si470x: Fix DRIVER_AUTHOR macro definition
  media: dvb-usb-v2: lmedm04: Fix firmware macro definitions
  modules: add rust modules files to MAINTAINERS
  rust: samples: add a module parameter to the rust_minimal sample
  rust: module: update the module macro with module parameter support
  rust: module: use a reference in macros::module::module
  rust: introduce module_param module
  rust: str: add radix prefixed integer parsing functions
  rust: sync: add `SetOnce`
2025-12-06 08:27:07 -08:00
David Laight
150215b89b drivers/xen: use min() instead of min_t()
min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
and so cannot discard significant bits.

In this case the 'unsigned long' value is small enough that the result
is ok.

Detected by an extra check added to min_t().

Signed-off-by: David Laight <david.laight.linux@gmail.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Message-ID: <20251119224140.8616-30-david.laight.linux@gmail.com>
Signed-off-by: Juergen Gross <jgross@suse.com>
2025-12-05 08:46:07 +01:00
Dan Williams
7dfbe9a675 crypto/ccp: Fix CONFIG_PCI=n build
It turns out that the PCI driver for ccp is unconditionally built into the
kernel in the CONFIG_PCI=y case. This means that the new SEV-TIO support
needs an explicit dependency on PCI to avoid build errors when
CONFIG_CRYPTO_DEV_SP_PSP=y and CONFIG_PCI=n.

Reported-by: kernel test robot <lkp@intel.com>
Closes: http://lore.kernel.org/202512030743.6pVPA4sx-lkp@intel.com
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: John Allen <john.allen@amd.com>
Acked-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251203031948.2471431-1-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-04 18:14:08 -08:00
Nathan Chancellor
311607017e virt: Fix Kconfig warning when selecting TSM without VIRT_DRIVERS
After commit 3225f52cde ("PCI/TSM: Establish Secure Sessions and Link
Encryption"), there is a Kconfig warning when selecting CONFIG_TSM
without CONFIG_VIRT_DRIVERS:

  WARNING: unmet direct dependencies detected for TSM
    Depends on [n]: VIRT_DRIVERS [=n]
    Selected by [y]:
    - PCI_TSM [=y] && PCI [=y]

CONFIG_TSM is defined in drivers/virt/coco/Kconfig but this Kconfig is
only sourced when CONFIG_VIRT_DRIVERS is enabled. Since this symbol is
hidden with no dependencies, it should be available without a symbol
that just enables a menu.

Move the sourcing of drivers/virt/coco/Kconfig outside of
CONFIG_VIRT_DRIVERS and wrap the other source statements in
drivers/virt/coco/Kconfig with CONFIG_VIRT_DRIVERS to ensure users do
not get any additional prompts while ensuring CONFIG_TSM is always
available to select. This complements commit 110c155e8a ("drivers/virt:
Drop VIRT_DRIVERS build dependency"), which addressed the build issue
that this Kconfig warning was pointing out.

Fixes: 3225f52cde ("PCI/TSM: Establish Secure Sessions and Link Encryption")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202511140712.NubhamPy-lkp@intel.com/
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Link: https://patch.msgid.link/20251203-fix-pci-tsm-select-tsm-warning-v1-1-c3959c1cb110@kernel.org
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-04 17:34:16 -08:00
Bagas Sanjaya
df8c841dd9 NFSD: nfsd-io-modes: Separate lists
Sphinx reports htmldocs indentation warnings:

Documentation/filesystems/nfs/nfsd-io-modes.rst:58: ERROR: Unexpected indentation. [docutils]
Documentation/filesystems/nfs/nfsd-io-modes.rst:59: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]

These caused the lists to be shown as long running paragraphs merged
with their previous paragraphs.

Fix these by separating the lists with a blank line.

Fixes: fa8d4e6784 ("NFSD: add Documentation/filesystems/nfs/nfsd-io-modes.rst")
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Closes: https://lore.kernel.org/linux-next/20251202152506.7a2d2d41@canb.auug.org.au/
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-03 09:05:14 -05:00
Bagas Sanjaya
4fcf9952fb NFSD: nfsd-io-modes: Wrap shell snippets in literal code blocks
Sphinx reports htmldocs indentation warnings:

Documentation/filesystems/nfs/nfsd-io-modes.rst:29: ERROR: Unexpected indentation. [docutils]
Documentation/filesystems/nfs/nfsd-io-modes.rst:34: ERROR: Unexpected indentation. [docutils]

Fix these by wrapping shell snippets in literal code blocks.

Fixes: fa8d4e6784 ("NFSD: add Documentation/filesystems/nfs/nfsd-io-modes.rst")
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Closes: https://lore.kernel.org/linux-next/20251202152506.7a2d2d41@canb.auug.org.au/
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-03 09:05:14 -05:00
Bagas Sanjaya
21478b6eca NFSD: Add toctree entry for NFSD IO modes docs
Commit fa8d4e6784 ("NFSD: add
Documentation/filesystems/nfs/nfsd-io-modes.rst") adds documentation for
NFSD I/O modes, but it forgets to add toctree entry for it. Hence,
Sphinx reports:

Documentation/filesystems/nfs/nfsd-io-modes.rst: WARNING: document isn't included in any toctree [toc.not_included]

Add the entry.

Fixes: fa8d4e6784 ("NFSD: add Documentation/filesystems/nfs/nfsd-io-modes.rst")
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Closes: https://lore.kernel.org/linux-next/20251202152506.7a2d2d41@canb.auug.org.au/
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-03 09:05:14 -05:00
Len Brown
9c0bad7508 tools/power turbostat: version 2025.12.02
Since release 2025.09.09:

Add LLC statistics columns:
    LLCkRPS = Last Level Cache Thousands of References Per Second
    LLC%hit = Last Level Cache Hit %
Recognize Wildcat Lake and Nova Lake platforms
Add MSR check for Android
Add APERF check for VMWARE
Add RAPL check for AWS
minor fixes

This patch:

White-space only, resulting from running Lindent
on everything except the tab-justified data-tables,
and using -l150 instead of -l80 to allow long lines.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
1a23ba6a1b tools/power turbostat: Print wide names only for RAW 64-bit columns
Print a wide column header only for the case of a 64-bit RAW counter.

It turns out that wide column headers otherwise are more harm than good.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
2ba8b24e9d tools/power turbostat: Print percentages in 8-columns
Added counters that are FORMAT_PERCENT
do not need to be 64-bits -- 32 is plenty.
This allows the output code to fit them,
and their header, into 8-columns.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
8808292799 tools/power turbostat: Print "nan" for out of range percentages
Sometimes counters return junk.
For the cases where values > 100% is invalid, print "nan".

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
951845d51d tools/power turbostat: Validate APERF access for VMWARE
VMWARE correctly enumerates lack of APERF and MPERF in CPUID,
but turbostat didn't consult that before attempting to access them.

Since VMWARE allows access, but always returns 0, turbostat
got confusd into an infinite reset loop.

Head this off by listening to CPUID.6.APERF_MPERF
(and rename the existing variable to make this more clear)

Reported-by: David Arcari <darcari@redhat.com>
Tested-by: David Arcari <darcari@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
68769a0b5a tools/power turbostat: Enhance perf probe
check_perf_access() will now check both IPC and LLC perf counters
if they are enabled.  If any fail, it now disables perf
and all perf counters.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Len Brown
19476a592b tools/power turbostat: Validate RAPL MSRs for AWS Nitro Hypervisor
Even though the platform->plat_rapl_msrs enumeration may be accurate,
a VM, such as AWS Nitro Hypervisor, may deny access to the underlying MSRs.

Probe if PKG_ENERGY is readable and non-zero.
If no, ignore all RAPL MSRs.

Reported-by: Emily Ehlert <ehemily@amazon.de>
Tested-by: Emily Ehlert <ehemily@amazon.de>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:14 -05:00
Malaya Kumar Rout
51860d6330 tools/power x86_energy_perf_policy: Fix potential NULL pointer dereference
In err_on_hypervisor(), strstr() is called to search for "flags" in the
buffer, but the return value is not checked before being used in pointer
arithmetic (flags - buffer). If strstr() returns NULL because "flags" is
not found in /proc/cpuinfo, this will cause undefined behavior and likely
a crash.

Add a NULL check after the strstr() call and handle the error appropriately
by cleaning up resources and reporting a meaningful error message.

Signed-off-by: Malaya Kumar Rout <mrout@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 16:11:09 -05:00
Malaya Kumar Rout
7446bd6119 tools/power x86_energy_perf_policy: Fix format string in error message
The error message in validate_cpu_selected_set() uses an incomplete
format specifier "cpu%" instead of "cpu%d", resulting in the error
message printing "Requested cpu% is not present" rather than
showing the actual CPU number.

Fix the format string to properly display the CPU number.

Signed-off-by: Malaya Kumar Rout <mrout@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:30 -05:00
Len Brown
90a2fe2576 tools/power x86_energy_perf_policy: Simplify Android MSR probe
no functional change

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:30 -05:00
Kaushlendra Kumar
2ff4b59f2e tools/power x86_energy_perf_policy: Add Android MSR device support
Add support for Android MSR device paths which use /dev/msrN format
instead of the standard Linux /dev/cpu/N/msr format. The tool now
probes both path formats at startup and uses the appropriate one.

This enables x86_energy_perf_policy to work on Android systems where
MSR devices follow a different naming convention while maintaining
full compatibility with standard Linux systems.

Signed-off-by: Kaushlendra Kumar <kaushlendra.kumar@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:30 -05:00
Len Brown
d71cb404f0 tools/power turbostat: Add run-time MSR driver probe
Rather than starting down the conditional-compile road...

Probe the location of the MSR files at run-time.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:30 -05:00
Emily Ehlert
2313b97bc0 tools/power turbostat: Set per_cpu_msr_sum to NULL after free
Set per_cpu_msr_sum to NULL after freeing it in the error path
of msr_sum_record() to prevent potential use-after-free issues.

Signed-off-by: Emily Ehlert <ehemily@amazon.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:30 -05:00
Len Brown
28a3ad1fd2 tools/power turbostat: Add LLC stats
LLCkRPS = Last Level Cache Thousands of References Per Second
LLC%hit = Last Level Cache Hit %

These columns are enabled by-default.
They can be controlled with the --show/--hide options
by individual column names above,
or together using the "llc" or "cache" groups.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-12-02 15:58:23 -05:00
Alexey Kardashevskiy
4be423572d crypto/ccp: Implement SEV-TIO PCIe IDE (phase1)
Implement the SEV-TIO (Trusted I/O) firmware interface for PCIe TDISP
(Trust Domain In-Socket Protocol). This enables secure communication
between trusted domains and PCIe devices through the PSP (Platform
Security Processor).

The implementation includes:
- Device Security Manager (DSM) operations for establishing secure links
- SPDM (Security Protocol and Data Model) over DOE (Data Object Exchange)
- IDE (Integrity Data Encryption) stream management for secure PCIe

This module bridges the SEV firmware stack with the generic PCIe TSM
framework.

This is phase1 as described in Documentation/driver-api/pci/tsm.rst.

On AMD SEV, the AMD PSP firmware acts as TSM (manages the security/trust).
The CCP driver provides the interface to it and registers in the TSM
subsystem.

Detect the PSP support (reported via FEATURE_INFO + SNP_PLATFORM_STATUS)
and enable SEV-TIO in the SNP_INIT_EX call if the hardware supports TIO.

Implement SEV TIO PSP command wrappers in sev-dev-tio.c and store
the data in the SEV-TIO-specific structs.

Implement TSM hooks and IDE setup in sev-dev-tsm.c.

Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/692f506bb80c9_261c11004@dwillia2-mobl4.notmuch
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-02 12:50:33 -08:00
Alexey Kardashevskiy
eeb934137d iommu/amd: Report SEV-TIO support
The SEV-TIO switch in the AMD BIOS is reported to the OS via
the IOMMU Extended Feature 2 register (EFR2), bit 1.

Add helper to parse the bit and report the feature presence.

Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251202024449.542361-4-aik@amd.com
Acked-by: Joerg Roedel <joerg.roedel@amd.com>
Reviewed-by: Vasant Hegde <vasant.hegde@amd.com>
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-02 12:06:45 -08:00
Alexey Kardashevskiy
c3859de858 psp-sev: Assign numbers to all status codes and add new
Make the definitions explicit. Add some more new codes.

The following patches will be using SPDM_REQUEST and
EXPAND_BUFFER_LENGTH_REQUEST, others are useful for the PSP FW
diagnostics.

Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251202024449.542361-3-aik@amd.com
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-02 12:06:38 -08:00
Alexey Kardashevskiy
8a5dd102e4 ccp: Make snp_reclaim_pages and __sev_do_cmd_locked public
The snp_reclaim_pages() helper reclaims pages in the FW state. SEV-TIO
and the TMPM driver (a hardware engine which smashes IOMMU PDEs among
other things) will use to reclaim memory when cleaning up.

Share and export snp_reclaim_pages().

Most of the SEV-TIO code uses sev_do_cmd() which locks the sev_cmd_mutex
and already exported. But the SNP init code (which also sets up SEV-TIO)
executes under the sev_cmd_mutex lock so the SEV-TIO code has to use
the __sev_do_cmd_locked() helper. This one though does not need to be
exported/shared globally as SEV-TIO is a part of the CCP driver still.

Share __sev_do_cmd_locked() via the CCP internal header.

Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251202024449.542361-2-aik@amd.com
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-12-02 12:05:51 -08:00
Alice Ryhl
5ba71195a9 rust_binder: use bitmap for allocation of handles
To find an unused Binder handle, Rust Binder currently iterates the
red/black tree from the beginning until it finds a gap in the keys. This
is extremely slow.

To improve the performance, add a bitmap that keeps track of which
indices are actually in use. This allows us to quickly find an unused
key in the red/black tree.

For a benchmark, please see the below numbers that were obtained from
modifying binderThroughputTest to send a node with each transaction and
stashing it in the server. This results in the number of nodes
increasing by one for every transaction sent. I got the following table
of roundtrip latencies (in µs):

Transaction Range │ Baseline (Rust) │ Bitmap (Rust) │ Comparison (C)
0 - 10,000        │          176.88 │         92.93 │          99.41
10,000 - 20,000   │          437.37 │         87.74 │          98.55
20,000 - 30,000   │          677.49 │         76.24 │          96.37
30,000 - 40,000   │          901.76 │         83.39 │          96.73
40,000 - 50,000   │         1126.62 │        100.44 │          94.57
50,000 - 60,000   │         1288.98 │         94.38 │          96.64
60,000 - 70,000   │         1588.74 │         88.27 │          96.36
70,000 - 80,000   │         1812.97 │         93.97 │          91.24
80,000 - 90,000   │         2062.95 │         92.22 │         102.01
90,000 - 100,000  │         2330.03 │         97.18 │         100.31

It should be clear that the current Rust code becomes linearly slower
per insertion as the number of calls to rb_next() per transaction
increases. After this change, the time to find an ID number appears
constant. (Technically it is not constant-time as both insertion and
removal scan the entire bitmap. However, quick napkin math shows that
scanning the entire bitmap with N=100k takes ~1.5µs, which is neglible
in a benchmark where the rountrip latency is 100µs.)

I've included a comparison to the C driver, which uses the same bitmap
algorithm as this patch since commit 15d9da3f81 ("binder: use bitmap
for faster descriptor lookup").

This currently checks if the bitmap should be shrunk after every
removal. One potential future change is introducing a shrinker to make
this operation O(1), but based on the benchmark above this does not seem
required at this time.

Reviewed-by: Burak Emir <bqe@google.com>
Reviewed-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
Acked-by: Carlos Llamas <cmllamas@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-12-02 14:17:47 -05:00
Alice Ryhl
f523d110a6 rust: id_pool: do not immediately acquire new ids
When Rust Binder assigns a new ID, it performs various fallible
operations before it "commits" to actually using the new ID. To support
this pattern, change acquire_next_id() so that it does not immediately
call set_bit(), but instead returns an object that may be used to call
set_bit() later.

The UnusedId type holds a exclusive reference to the IdPool, so it's
guaranteed that nobody else can call find_unused_id() while the UnusedId
object is live.

[Miguel: rust: id_pool: fix example]

Reviewed-by: Burak Emir <bqe@google.com>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-12-02 14:17:09 -05:00
Alice Ryhl
69ec6a1bed rust: id_pool: do not supply starting capacity
Rust Binder wants to use inline bitmaps whenever possible to avoid
allocations, so introduce a constructor for an IdPool with arbitrary
capacity that stores the bitmap inline.

The existing constructor could be renamed to with_capacity() to match
constructors for other similar types, but it is removed as there is
currently no user for it.

[Miguel: rust: id_pool: fix broken intra-doc link]

Acked-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
Reviewed-by: Burak Emir <bqe@google.com>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-12-02 14:14:50 -05:00
Mike Snitzer
fa8d4e6784 NFSD: add Documentation/filesystems/nfs/nfsd-io-modes.rst
This document details the NFSD IO modes that are configurable using
NFSD's experimental debugfs interfaces:

  /sys/kernel/debug/nfsd/io_cache_read
  /sys/kernel/debug/nfsd/io_cache_write

This document will evolve as NFSD's interfaces do (e.g. if/when NFSD's
debugfs interfaces are replaced with per-export controls).

Future updates will provide more specific guidance and howto
information to help others use and evaluate NFSD's IO modes:
BUFFERED, DONTCACHE and DIRECT.

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-01 09:57:10 -05:00
Mike Snitzer
06c5c97293 NFSD: Implement NFSD_IO_DIRECT for NFS WRITE
When NFSD_IO_DIRECT is selected via the
/sys/kernel/debug/nfsd/io_cache_write experimental tunable, split
incoming unaligned NFS WRITE requests into a prefix, middle and
suffix segment, as needed. The middle segment is now DIO-aligned and
the prefix and/or suffix are unaligned. Synchronous buffered IO is
used for the unaligned segments, and IOCB_DIRECT is used for the
middle DIO-aligned extent.

Although IOCB_DIRECT avoids the use of the page cache, by itself it
doesn't guarantee data durability. For UNSTABLE WRITE requests,
durability is obtained by a subsequent NFS COMMIT request.

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Co-developed-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-01 09:57:10 -05:00
Chuck Lever
e3e8e176ca NFSD: Make FILE_SYNC WRITEs comply with spec
Mike noted that when NFSD responds to an NFS_FILE_SYNC WRITE, it
does not also persist file time stamps. To wit, Section 18.32.3
of RFC 8881 mandates:

> The client specifies with the stable parameter the method of how
> the data is to be processed by the server. If stable is
> FILE_SYNC4, the server MUST commit the data written plus all file
> system metadata to stable storage before returning results. This
> corresponds to the NFSv2 protocol semantics. Any other behavior
> constitutes a protocol violation. If stable is DATA_SYNC4, then
> the server MUST commit all of the data to stable storage and
> enough of the metadata to retrieve the data before returning.

Commit 3f3503adb3 ("NFSD: Use vfs_iocb_iter_write()") replaced:

-		flags |= RWF_SYNC;

with:

+		kiocb.ki_flags |= IOCB_DSYNC;

which appears to be correct given:

	if (flags & RWF_SYNC)
		kiocb_flags |= IOCB_DSYNC;

in kiocb_set_rw_flags(). However the author of that commit did not
appreciate that the previous line in kiocb_set_rw_flags() results
in IOCB_SYNC also being set:

	kiocb_flags |= (__force int) (flags & RWF_SUPPORTED);

RWF_SUPPORTED contains RWF_SYNC, and RWF_SYNC is the same bit as
IOCB_SYNC. Reviewers at the time did not catch the omission.

Reported-by: Mike Snitzer <snitzer@kernel.org>
Closes: https://lore.kernel.org/linux-nfs/20251018005431.3403-1-cel@kernel.org/T/#t
Fixes: 3f3503adb3 ("NFSD: Use vfs_iocb_iter_write()")
Cc: stable@vger.kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-12-01 09:57:10 -05:00
Alexandru Gagniuc
641092c1bc remoteproc: qcom_q6v5_wcss: use optional reset for wcss_q6_bcr_reset
The "wcss_q6_bcr_reset" is not used on IPQ8074, and IPQ6018. Use
devm_reset_control_get_optional_exclusive() for this reset so that
probe() does not fail on platforms where it is not used.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://lore.kernel.org/r/20251129013207.3981517-2-mr.nuke.me@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 15:20:23 -06:00
Alexandru Gagniuc
7e81fa8d80 remoteproc: qcom_q6v5_wcss: fix parsing of qcom,halt-regs
The "qcom,halt-regs" consists of a phandle reference followed by the
three offsets within syscon for halt registers. Thus, we need to
request 4 integers from of_property_read_variable_u32_array(), with
the halt_reg ofsets at indexes 1, 2, and 3. Offset 0 is the phandle.

With MAX_HALT_REG at 3, of_property_read_variable_u32_array() returns
-EOVERFLOW, causing .probe() to fail.

Increase MAX_HALT_REG to 4, and update the indexes accordingly.

Fixes: 0af65b9b91 ("remoteproc: qcom: wcss: Add non pas wcss Q6 support for QCS404")
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://lore.kernel.org/r/20251129013207.3981517-1-mr.nuke.me@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 15:19:45 -06:00
Dan Carpenter
cda5dc12eb remoteproc: qcom_wcnss: Fix NULL vs IS_ERR() bug in wcnss_alloc_memory_region()
The devm_ioremap_resource_wc() function never returns NULL, it returns
error pointers.  Update the checking to match.

Fixes: c70b9d5fdc ("remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Link: https://lore.kernel.org/r/09a43da41ee277a80a3265348831e747f7b62620.1764427595.git.dan.carpenter@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 14:18:23 -06:00
Dan Carpenter
cb200e41ed remoteproc: qcom: q6v5: Fix NULL vs IS_ERR() bug in q6v5_alloc_memory_region()
The devm_ioremap_resource_wc() function never returns NULL, it returns
error pointers.  Update the checking to match.

Fixes: c70b9d5fdc ("remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Link: https://lore.kernel.org/r/674b32a78563282adeaf3cdf941314a0b8181026.1764427595.git.dan.carpenter@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 14:18:23 -06:00
Dan Carpenter
e7839f773e remoteproc: qcom: pas: Fix a couple NULL vs IS_ERR() bugs
The devm_ioremap_resource_wc() function never returns NULL, it returns
error pointers.  Update the checking to match.

Fixes: c70b9d5fdc ("remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Link: https://lore.kernel.org/r/0599691acc394d9390da8fa0b5de3399b132b187.1764427595.git.dan.carpenter@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 14:18:23 -06:00
Dan Carpenter
5e6fee736e remoteproc: qcom_q6v5_adsp: Fix a NULL vs IS_ERR() check in adsp_alloc_memory_region()
The devm_ioremap_resource_wc() function never returns NULL, it returns
error pointers.  Update the check to match.

Fixes: c70b9d5fdc ("remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Link: https://lore.kernel.org/r/6d6b1b0fb6a61b5155a640507217fd7e658858cf.1764427595.git.dan.carpenter@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-29 14:18:23 -06:00
Mickaël Salaün
54f9baf537 selftests/landlock: Add disconnected leafs and branch test suites
Test disconnected directories with two test suites
(layout4_disconnected_leafs and layout5_disconnected_branch) and 43
variants to cover the main corner cases.

These tests are complementary to the previous commit.

Add test_renameat() and test_exchangeat() helpers.

Test coverage for security/landlock is 92.1% of 1927 lines according to
LLVM 20.

Cc: Günther Noack <gnoack@google.com>
Cc: Song Liu <song@kernel.org>
Cc: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/20251128172200.760753-5-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-28 18:27:07 +01:00
Tingmao Wang
a18ee3f31f selftests/landlock: Add tests for access through disconnected paths
This adds tests for the edge case discussed in [1], with specific ones
for rename and link operations when the operands are through
disconnected paths, as that go through a separate code path in Landlock.

This has resulted in a warning, due to collect_domain_accesses() not
expecting to reach a different root from path->mnt:

  #  RUN           layout1_bind.path_disconnected ...
  #            OK  layout1_bind.path_disconnected
  ok 96 layout1_bind.path_disconnected
  #  RUN           layout1_bind.path_disconnected_rename ...
  [..] ------------[ cut here ]------------
  [..] WARNING: CPU: 3 PID: 385 at security/landlock/fs.c:1065 collect_domain_accesses
  [..] ...
  [..] RIP: 0010:collect_domain_accesses (security/landlock/fs.c:1065 (discriminator 2) security/landlock/fs.c:1031 (discriminator 2))
  [..] current_check_refer_path (security/landlock/fs.c:1205)
  [..] ...
  [..] hook_path_rename (security/landlock/fs.c:1526)
  [..] security_path_rename (security/security.c:2026 (discriminator 1))
  [..] do_renameat2 (fs/namei.c:5264)
  #            OK  layout1_bind.path_disconnected_rename
  ok 97 layout1_bind.path_disconnected_rename

Move the const char definitions a bit above so that we can use the path
for s4d1 in cleanup code.

Cc: Günther Noack <gnoack@google.com>
Cc: Song Liu <song@kernel.org>
Link: https://lore.kernel.org/r/027d5190-b37a-40a8-84e9-4ccbc352bcdf@maowtm.org [1]
Signed-off-by: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/20251128172200.760753-4-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-28 18:27:06 +01:00
Mickaël Salaün
f7ef7de6b9 landlock: Improve variable scope
This is now possible thanks to the disconnected directory fix.

Cc: Günther Noack <gnoack@google.com>
Cc: Song Liu <song@kernel.org>
Cc: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/20251128172200.760753-3-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-28 18:27:06 +01:00
Mickaël Salaün
49c9e09d96 landlock: Fix handling of disconnected directories
Disconnected files or directories can appear when they are visible and
opened from a bind mount, but have been renamed or moved from the source
of the bind mount in a way that makes them inaccessible from the mount
point (i.e. out of scope).

Previously, access rights tied to files or directories opened through a
disconnected directory were collected by walking the related hierarchy
down to the root of the filesystem, without taking into account the
mount point because it couldn't be found. This could lead to
inconsistent access results, potential access right widening, and
hard-to-debug renames, especially since such paths cannot be printed.

For a sandboxed task to create a disconnected directory, it needs to
have write access (i.e. FS_MAKE_REG, FS_REMOVE_FILE, and FS_REFER) to
the underlying source of the bind mount, and read access to the related
mount point.   Because a sandboxed task cannot acquire more access
rights than those defined by its Landlock domain, this could lead to
inconsistent access rights due to missing permissions that should be
inherited from the mount point hierarchy, while inheriting permissions
from the filesystem hierarchy hidden by this mount point instead.

Landlock now handles files and directories opened from disconnected
directories by taking into account the filesystem hierarchy when the
mount point is not found in the hierarchy walk, and also always taking
into account the mount point from which these disconnected directories
were opened.  This ensures that a rename is not allowed if it would
widen access rights [1].

The rationale is that, even if disconnected hierarchies might not be
visible or accessible to a sandboxed task, relying on the collected
access rights from them improves the guarantee that access rights will
not be widened during a rename because of the access right comparison
between the source and the destination (see LANDLOCK_ACCESS_FS_REFER).
It may look like this would grant more access on disconnected files and
directories, but the security policies are always enforced for all the
evaluated hierarchies.  This new behavior should be less surprising to
users and safer from an access control perspective.

Remove a wrong WARN_ON_ONCE() canary in collect_domain_accesses() and
fix the related comment.

Because opened files have their access rights stored in the related file
security properties, there is no impact for disconnected or unlinked
files.

Cc: Christian Brauner <brauner@kernel.org>
Cc: Günther Noack <gnoack@google.com>
Cc: Song Liu <song@kernel.org>
Reported-by: Tingmao Wang <m@maowtm.org>
Closes: https://lore.kernel.org/r/027d5190-b37a-40a8-84e9-4ccbc352bcdf@maowtm.org
Closes: https://lore.kernel.org/r/09b24128f86973a6022e6aa8338945fcfb9a33e4.1749925391.git.m@maowtm.org
Fixes: b91c3e4ea7 ("landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER")
Fixes: cb2c7d1a17 ("landlock: Support filesystem access-control")
Link: https://lore.kernel.org/r/b0f46246-f2c5-42ca-93ce-0d629702a987@maowtm.org [1]
Reviewed-by: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/20251128172200.760753-2-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-28 18:27:04 +01:00
Dan Carpenter
099a60cca1 remoteproc: imx_dsp_rproc: Fix NULL vs IS_ERR() bug in imx_dsp_rproc_add_carveout()
The devm_ioremap_resource_wc() function never returns NULL, it returns
error pointers.  Update the error checking to match.

Fixes: 67a7bc7f03 ("remoteproc: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Link: https://lore.kernel.org/r/aSf6OerBbPcxBUVt@stanley.mountain
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-27 08:52:54 -07:00
Bjorn Andersson
ac82dbc539 remoteproc: st: Fix indexing of memory-regions
The recent transition to use of_reserved_mem_region_to_resource()
changes the while loop to a for loop, but the increment of the "index"
variable was left behind at the end of the loop, as highlighted by the
following error/warning:

  error: variable 'index' is incremented both in the loop header and in the loop body [-Werror,-Wfor-loop-analysis]

Drop the extra increment to avoid skipping over every other
memory-region in the loop.

Fixes: 67a7bc7f03 ("remoteproc: Use of_reserved_mem_region_* functions for "memory-region"")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20251126-st-remoteproc-double-index-v1-1-3b0a8b21ac18@oss.qualcomm.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-27 08:35:19 -07:00
Matthieu Buffet
e61462232a selftests/landlock: Fix makefile header list
Make all headers part of make's dependencies computations.
Otherwise, updating audit.h, common.h, scoped_base_variants.h,
scoped_common.h, scoped_multiple_domain_variants.h, or wrappers.h,
re-running make and running selftests could lead to testing stale headers.

Fixes: 6a500b2297 ("selftests/landlock: Add tests for audit flags and domain IDs")
Fixes: fefcf0f7cf ("selftests/landlock: Test abstract UNIX socket scoping")
Fixes: 5147779d5e ("selftests/landlock: Add wrappers.h")
Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
Link: https://lore.kernel.org/r/20251027011440.1838514-1-matthieu@buffet.re
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-26 20:20:23 +01:00
Tingmao Wang
335ef80e4a landlock: Make docs in cred.h and domain.h visible
Currently even though the structures in these files have documentation,
they are not shown in the "Landlock LSM: kernel documentation" page.

Signed-off-by: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/6050e764c2679cba01715653e5f1f4f17091d8f8.1759103277.git.m@maowtm.org
[mic: Synchronize date]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-26 20:20:23 +01:00
Tingmao Wang
f4d3ef2dd0 landlock: Minor comments improvements
This patch contains some small comment changes.  The first three
comments for ruleset.c, I sort of made along the way while working on /
trying to understand Landlock, and the one from ruleset.h was from the
hashtable patch but extracted here.  In fs.c, one comment which I found
would have been helpful to me when reading this.

Signed-off-by: Tingmao Wang <m@maowtm.org>
Link: https://lore.kernel.org/r/20250602134150.67189-1-m@maowtm.org
Link: https://lore.kernel.org/r/20297185fd71ffbb5ce4fec14b38e5444c719c96.1748379182.git.m@maowtm.org
[mic: Squash patches with updated description, cosmetic fixes]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
2025-11-26 20:20:21 +01:00
Nickolay Goppen
950c74fd6c remoteproc: qcom: pas: Add support for SDM660 CDSP
Compute DSP in SDM660 is compatible with generic cdsp_resource_init
descriptor.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Tested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> # ifc6560
Signed-off-by: Nickolay Goppen <setotau@mainlining.org>
Link: https://lore.kernel.org/r/20251110-qcom-sdm660-cdsp-v3-3-cc3c37287e72@mainlining.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 11:41:01 -06:00
Nickolay Goppen
acd6c28a25 dt-bindings: remoteproc: qcom: adsp: Add SDM660 CDSP compatible
Add compatible for the compute DSP remoteproc found in SDM660.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Nickolay Goppen <setotau@mainlining.org>
Link: https://lore.kernel.org/r/20251110-qcom-sdm660-cdsp-v3-2-cc3c37287e72@mainlining.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 11:41:01 -06:00
Nickolay Goppen
db03780e43 dt-bindings: remoteproc: qcom: adsp: Add missing constrains for SDM660 ADSP
Since SDM660 ADSP node uses "xo" clock, interrupts and "cx" power domain
properties add corresponding constrains for SDM660 ADSP.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Nickolay Goppen <setotau@mainlining.org>
Link: https://lore.kernel.org/r/20251110-qcom-sdm660-cdsp-v3-1-cc3c37287e72@mainlining.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 11:41:00 -06:00
Konrad Dybcio
ca079ec3eb dt-bindings: remoteproc: qcom,sc8280xp-pas: Fix CDSP power desc
The power requirements for the CDSP instances on SC8280XP aren't fully
described, with only one of the three present. Fix that.

Fixes: ee651cd1e9 ("dt-bindings: remoteproc: qcom: pas: Add sc8280xp adsp and nsp pair")
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20251104-topic-8280_mxc-v1-2-df545af0ef94@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 11:40:31 -06:00
Sakari Ailus
7f07a5c3e2 remoteproc: omap: Remove redundant pm_runtime_mark_last_busy() calls
pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(),
pm_runtime_autosuspend() and pm_request_autosuspend() now include a call
to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to
pm_runtime_mark_last_busy().

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Link: https://lore.kernel.org/r/20250704075445.3221481-1-sakari.ailus@linux.intel.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 11:37:44 -06:00
Dan Carpenter
30065e73d7 nvdimm: Prevent integer overflow in ramdax_get_config_data()
The "cmd->in_offset" variable comes from the user via the __nd_ioctl()
function.  The problem is that the "cmd->in_offset + cmd->in_length"
addition could have an integer wrapping issue if cmd->in_offset is close
to UINT_MAX .  Both "cmd->in_offset" and "cmd->in_length" are u32
variables.

Fixes: 43bc0aa19a ("nvdimm: allow exposing RAM carveouts as NVDIMM DIMM devices")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Link: https://patch.msgid.link/aSbuiYCznEIZDa02@stanley.mountain
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2025-11-26 10:58:23 -06:00
Rob Herring (Arm)
c70b9d5fdc remoteproc: qcom: Use of_reserved_mem_region_* functions for "memory-region"
Use the newly added of_reserved_mem_region_to_resource() and
of_reserved_mem_region_count() functions to handle "memory-region"
properties.

The error handling is a bit different in some cases. Often
"memory-region" is optional, so failed lookup is not an error. But then
an error in of_reserved_mem_lookup() is treated as an error. However,
that distinction is not really important. Either the region is available
and usable or it is not. So now, it is just
of_reserved_mem_region_to_resource() which is checked for an error.

Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Link: https://lore.kernel.org/r/20251124182751.507624-2-robh@kernel.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 10:27:29 -06:00
Alice Ryhl
6297fb3863 rust: id_pool: rename IdPool::new() to with_capacity()
We want to change ::new() to take no parameters and produce a pool that
is as large as possible while also being inline because that is the
constructor that Rust Binder actually needs.

However, to avoid complications in examples, we still need the current
constructor. So rename it to with_capacity(), which is the idiomatic
Rust name for this kind constructor.

Reviewed-by: Burak Emir <bqe@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-26 11:25:35 -05:00
Alice Ryhl
d0cf6512bb rust: bitmap: add BitmapVec::new_inline()
This constructor is useful when you just want to create a BitmapVec
without allocating but don't care how large it is.

Acked-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
Reviewed-by: Burak Emir <bqe@google.com>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-26 11:25:35 -05:00
Alice Ryhl
f5535d78e1 rust: bitmap: add MAX_LEN and MAX_INLINE_LEN constants
To avoid hard-coding these values in drivers, define constants for them
that drivers can reference. Also, update all instances in bitmap.rs and
id_pool.rs that use these values to use the new constants.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Burak Emir <bqe@google.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-26 11:25:35 -05:00
Srinivas Kandagatla
112766cdf2 rpmsg: glink: remove duplicate code for rpmsg device remove
rpmsg device remove code is duplicated in at-least 2-3 places, add a
helper function to remove this duplicated code.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250822100043.2604794-3-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 10:16:10 -06:00
Srinivas Kandagatla
a53e356df5 rpmsg: glink: fix rpmsg device leak
While testing rpmsg-char interface it was noticed that duplicate sysfs
entries are getting created and below warning is noticed.

Reason for this is that we are leaking rpmsg device pointer, setting it
null without actually unregistering device.
Any further attempts to unregister fail because rpdev is NULL,
resulting in a leak.

Fix this by unregistering rpmsg device before removing its reference
from rpmsg channel.

sysfs: cannot create duplicate filename '/devices/platform/soc@0/3700000.remot
eproc/remoteproc/remoteproc1/3700000.remoteproc:glink-edge/3700000.remoteproc:
glink-edge.adsp_apps.-1.-1'
[  114.115347] CPU: 0 UID: 0 PID: 9 Comm: kworker/0:0 Not
 tainted 6.16.0-rc4 #7 PREEMPT
[  114.115355] Hardware name: Qualcomm Technologies, Inc. Robotics RB3gen2 (DT)
[  114.115358] Workqueue: events qcom_glink_work
[  114.115371] Call trace:8
[  114.115374]  show_stack+0x18/0x24 (C)
[  114.115382]  dump_stack_lvl+0x60/0x80
[  114.115388]  dump_stack+0x18/0x24
[  114.115393]  sysfs_warn_dup+0x64/0x80
[  114.115402]  sysfs_create_dir_ns+0xf4/0x120
[  114.115409]  kobject_add_internal+0x98/0x260
[  114.115416]  kobject_add+0x9c/0x108
[  114.115421]  device_add+0xc4/0x7a0
[  114.115429]  rpmsg_register_device+0x5c/0xb0
[  114.115434]  qcom_glink_work+0x4bc/0x820
[  114.115438]  process_one_work+0x148/0x284
[  114.115446]  worker_thread+0x2c4/0x3e0
[  114.115452]  kthread+0x12c/0x204
[  114.115457]  ret_from_fork+0x10/0x20
[  114.115464] kobject: kobject_add_internal failed for 3700000.remoteproc:
glink-edge.adsp_apps.-1.-1 with -EEXIST, don't try to register things with
the same name in the same directory.
[  114.250045] rpmsg 3700000.remoteproc:glink-edge.adsp_apps.-1.-1:
device_add failed: -17

Fixes: 835764ddd9 ("rpmsg: glink: Move the common glink protocol implementation to glink_native.c")
Cc: Stable@vger.kernel.org
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250822100043.2604794-2-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 10:16:05 -06:00
Luca Weiss
a1f2c2d55a remoteproc: qcom_q6v5_pas: Use resource with CX PD for MSM8974
MSM8974 requires the CX power domain, so use the msm8996_adsp_resource
which has cx under proxy_pd_names and is otherwise equivalent.

Signed-off-by: Luca Weiss <luca@lucaweiss.eu>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250621-msm8974-rpmpd-switch-v1-2-0a2cb303c446@lucaweiss.eu
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 10:12:03 -06:00
Luca Weiss
3d447dcdae dt-bindings: remoteproc: qcom,adsp: Make msm8974 use CX as power domain
Using CX as a regulator is an artifact of earlier times. Instead use CX
power rail as power domain from rpmpd.

Signed-off-by: Luca Weiss <luca@lucaweiss.eu>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250621-msm8974-rpmpd-switch-v1-1-0a2cb303c446@lucaweiss.eu
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
2025-11-26 10:11:49 -06:00
Rob Herring (Arm)
67a7bc7f03 remoteproc: Use of_reserved_mem_region_* functions for "memory-region"
Use the newly added of_reserved_mem_region_to_resource() and
of_reserved_mem_region_count() functions to handle "memory-region"
properties.

The error handling is a bit different in some cases. Often
"memory-region" is optional, so failed lookup is not an error. But then
an error in of_reserved_mem_lookup() is treated as an error. However,
that distinction is not really important. Either the region is available
and usable or it is not. So now, it is just
of_reserved_mem_region_to_resource() which is checked for an error.

Acked-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Tested-by: Peng Fan <peng.fan@nxp.com> # i.MX93-11x11-EVK for imx_rproc.c
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> # rcar
Tested-by: Beleswar Padhi <b-padhi@ti.com> # TI
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Link: https://lore.kernel.org/r/20251124182751.507624-1-robh@kernel.org
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-25 08:34:31 -07:00
Dai Ngo
99f5aa14f0 NFSD: Add trace point for SCSI fencing operation.
Add trace point to print client IP address, net namespace number,
device name and status of SCSI pr_preempt command.

Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Dai Ngo
6f52063db9 NFSD: use correct reservation type in nfsd4_scsi_fence_client
The reservation type argument for the pr_preempt call should match the
one used in nfsd4_block_get_device_info_scsi.

Fixes: f99d4fbdae ("nfsd: add SCSI layout support")
Cc: stable@vger.kernel.org
Signed-off-by: Dai Ngo <dai.ngo@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Chuck Lever
1c873a2fd1 xdrgen: Don't generate unnecessary semicolon
The Jinja2 templates add a semicolon at the end of every function.
The C language does not require this punctuation.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Chuck Lever
f7cb94fad4 xdrgen: Fix union declarations
Add a missing template file. This file is used when a union is
defined as a public API (ie, "pragma public <union name>;").

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Olga Kornievskaia
14282cc3cf NFSD: don't start nfsd if sv_permsocks is empty
Previously, while trying to create a server instance, if no
listening sockets were present then default parameter udp
and tcp listeners were created. It's unclear what purpose
was of starting these listeners were and how this could have
been triggered by the userland setup. This patch proposed
to ensure the reverse that we never end in a situation where
no listener sockets are created and we are trying to create
nfsd threads.

The problem it solves is: when nfs.conf only has tcp=n (and
nothing else for the choice of transports), nfsdctl would
still start the server and create udp and tcp listeners.

Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Khushal Chitturi
b0f8e1f1f5 xdrgen: handle _XdrString in union encoder/decoder
Running xdrgen on xdrgen/tests/test.x fails when
generating encoder or decoder functions for union
members of type _XdrString. It was because _XdrString
does not have a spec attribute like _XdrBasic,
leading to AttributeError.

This patch updates emit_union_case_spec_definition
and emit_union_case_spec_decoder/encoder to handle
_XdrString by assigning type_name = "char *" and
avoiding referencing to spec.

Testing: Fixed xdrgen tool was run on originally failing
test file (tools/net/sunrpc/xdrgen/tests/test.x) and now
completes without AttributeError. Modified xdrgen tool was
also run against nfs4_1.x (Documentation/sunrpc/xdr/nfs4_1.x).
The output header file matches with nfs4_1.h
(include/linux/sunrpc/xdrgen/nfs4_1.h).
This validates the patch for all XDR input files currently
within the kernel.

Changes since v2:
- Moved the shebang to the first line
- Removed SPDX header to match style of current xdrgen files

Changes since v1:
- Corrected email address in Signed-off-by.
- Wrapped patch description lines to 72 characters.

Signed-off-by: Khushal Chitturi <kc9282016@gmail.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Chuck Lever
42ba5bd2e2 xdrgen: Fix the variable-length opaque field decoder template
Ensure that variable-length opaques are decoded into the named
field, and do not overwrite the structure itself.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Chuck Lever
3bd937b49a xdrgen: Make the xdrgen script location-independent
The @pythondir@ placeholder is meant for build-time substitution,
such as with autoconf. autoconf is not used in the kernel. Let's
replace that mechanism with one that better enables the xdrgen
script to be run from any directory.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Chuck Lever
75a9b40f3b xdrgen: Generalize/harden pathname construction
Use Python's built-in Path constructor to find the Jinja templates.
This provides better error checking, proper use of path component
separators, and more reliable location of the template files.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-25 09:09:42 -05:00
Andy Shevchenko
4bd68e4753 cpumask: Don't use "proxy" headers
Update header inclusions to follow IWYU (Include What You Use)
principle.

Note that kernel.h is discouraged to be included as it's written
at the top of that file.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 16:08:18 -05:00
Geert Uytterhoeven
c604cb5fdf soc: renesas: Use bitfield helpers
Use the field_get() helper, instead of open-coding the same operation.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
3937b05bb7 clk: renesas: Use bitfield helpers
Use the FIELD_{GET,PREP}() and field_{get,prep}() helpers for const
respective non-const bitfields, instead of open-coding the same
operations.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
b1cff2f4b2 ALSA: usb-audio: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
610c4408a2 soc: renesas: rz-sysc: Convert to common field_get() helper
Drop the driver-specific field_get() macro, in favor of the globally
available variant from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
bb0e7fda87 pinctrl: ma35: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
1fe1c28a10 iio: mlx90614: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Crt Mori <cmo@melexis.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
54bfd90ca3 iio: dac: Convert to common field_prep() helper
Drop the driver-specific field_prep() macro, in favor of the globally
available variant from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
2ef26ba819 gpio: aspeed: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
331a1457d8 EDAC/ie31200: Convert to common field_get() helper
Drop the driver-specific field_get() macro, in favor of the globally
available variant from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
350f06c9e2 crypto: qat - convert to common field_get() helper
Drop the driver-specific field_get() macro, in favor of the globally
available variant from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
0f8407a1f1 clk: at91: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>
Acked-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:47 -05:00
Geert Uytterhoeven
c1c6ab80b2 bitfield: Add non-constant field_{prep,get}() helpers
The existing FIELD_{GET,PREP}() macros are limited to compile-time
constants.  However, it is very common to prepare or extract bitfield
elements where the bitfield mask is not a compile-time constant.

To avoid this limitation, the AT91 clock driver and several other
drivers already have their own non-const field_{prep,get}() macros.
Make them available for general use by adding them to
<linux/bitfield.h>, and improve them slightly:
  1. Avoid evaluating macro parameters more than once,
  2. Replace "ffs() - 1" by "__ffs()",
  3. Support 64-bit use on 32-bit architectures,
  4. Wire field_{get,prep}() to FIELD_{GET,PREP}() when mask is
     actually constant.

This is deliberately not merged into the existing FIELD_{GET,PREP}()
macros, as people expressed the desire to keep stricter variants for
increased safety, or for performance critical paths.

Yury: use __mask withing new macros.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Crt Mori <cmo@melexis.com>
Acked-by: Nuno Sá <nuno.sa@analog.com>
Acked-by: Richard Genoud <richard.genoud@bootlin.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
2a6c045640 bitfield: Add less-checking __FIELD_{GET,PREP}()
The BUILD_BUG_ON_MSG() check against "~0ull" works only with "unsigned
(long) long" _mask types.  For constant masks, that condition is usually
met, as GENMASK() yields an UL value.  The few places where the
constant mask is stored in an intermediate variable were fixed by
changing the variable type to u64 (see e.g. [1] and [2]).

However, for non-constant masks, smaller unsigned types should be valid,
too, but currently lead to "result of comparison of constant
18446744073709551615 with expression of type ... is always
false"-warnings with clang and W=1.

Hence refactor the __BF_FIELD_CHECK() helper, and factor out
__FIELD_{GET,PREP}().  The later lack the single problematic check, but
are otherwise identical to FIELD_{GET,PREP}(), and are intended to be
used in the fully non-const variants later.

[1] commit 5c667d5a5a ("clk: sp7021: Adjust width of _m in
    HWM_FIELD_PREP()")
[2] commit cfd6fb45cf ("crypto: ccree - avoid out-of-range
    warnings from clang")

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://git.kernel.org/torvalds/c/5c667d5a5a3ec166 [1]
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
85a8ff1185 ALSA: usb-audio: #undef field_{get,prep}() before local definition
Prepare for the advent of globally available common field_get() and
field_prep() macros by undefining the symbols before defining local
variants.  This prevents redefinition warnings from the C preprocessor
when introducing the common macros later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
138ab44108 soc: renesas: rz-sysc: #undef field_get() before local definition
Prepare for the advent of a globally available common field_get() macro
by undefining the symbol before defining a local variant.  This prevents
redefinition warnings from the C preprocessor when introducing the common
macro later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
2fc00c008e pinctrl: ma35: #undef field_{get,prep}() before local definition
Prepare for the advent of globally available common field_get() and
field_prep() macros by undefining the symbols before defining local
variants.  This prevents redefinition warnings from the C preprocessor
when introducing the common macros later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
8a838dabf1 iio: mlx90614: #undef field_{get,prep}() before local definition
Prepare for the advent of globally available common field_get() and
field_prep() macros by undefining the symbols before defining local
variants.  This prevents redefinition warnings from the C preprocessor
when introducing the common macros later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
27856d2b2b iio: dac: ad3530r: #undef field_prep() before local definition
Prepare for the advent of a globally available common field_prep() macro
by undefining the symbol before defining a local variant.  This prevents
redefinition warnings from the C preprocessor when introducing the common
macro later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
d1e1a7271e gpio: aspeed: #undef field_{get,prep}() before local definition
Prepare for the advent of globally available common field_get() and
field_prep() macros by undefining the symbols before defining local
variants.  This prevents redefinition warnings from the C preprocessor
when introducing the common macros later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
d51b09a0fe EDAC/ie31200: #undef field_get() before local definition
Prepare for the advent of a globally available common field_get() macro
by undefining the symbol before defining a local variant.  This prevents
redefinition warnings from the C preprocessor when introducing the common
macro later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
7996cbdb3f crypto: qat - #undef field_get() before local definition
Prepare for the advent of a globally available common field_get() macro
by undefining the symbol before defining a local variant.  This prevents
redefinition warnings from the C preprocessor when introducing the common
macro later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Geert Uytterhoeven
dbfe51513a clk: at91: pmc: #undef field_{get,prep}() before definition
Prepare for the advent of globally available common field_get() and
field_prep() macros by undefining the symbols before defining local
variants.  This prevents redefinition warnings from the C preprocessor
when introducing the common macros later.

Suggested-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>
Acked-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-24 14:15:46 -05:00
Jeff Layton
898f944652 lockd: don't allow locking on reexported NFSv2/3
Since commit 9254c8ae9b ("nfsd: disallow file locking and delegations
for NFSv4 reexport"), file locking when reexporting an NFS mount via
NFSv4 is expressly prohibited by nfsd. Do the same in lockd:

Add a new  nlmsvc_file_cannot_lock() helper that will test whether file
locking is allowed for a given file, and return nlm_lck_denied_nolocks
if it isn't.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Tested-by: Olga Kornievskaia <okorniev@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-20 16:29:35 -05:00
Christoph Hellwig
f6dcad1d74 MAINTAINERS: add a nfsd blocklayout reviewer
Add a minimal entry for the block layout driver to make sure Christoph
who wrote the code gets Cced on all patches.  The actual maintenance
stays with the nfsd maintainer team.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-20 16:29:35 -05:00
David Laight
d1cadd4bfc nodemask: use min() instead of min_t()
min_t(unsigned int, a, b) casts an 'unsigned long' to 'unsigned int'.
Use min(a, b) instead as it promotes any 'unsigned int' to 'unsigned long'
and so cannot discard significant bits.

In this case the 'unsigned long' value is small enough that the result
is ok.

Detected by an extra check added to min_t().

Signed-off-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-20 10:02:22 -05:00
Peng Fan
6f880e7bd1 remoteproc: imx_dsp_rproc: Simplify start/stop error handling
Replace goto-based error handling with early return pattern in
imx_dsp_rproc_{start,stop}() functions, and simplify if-else logic.

No functional changes, only code structure improvements for better
maintainability.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-12-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:16:55 -07:00
Peng Fan
5c33a631a5 remoteproc: imx_rproc: Remove enum imx_rproc_method
There is no user of enum imx_rproc_method after moved to ops based
method. Remove it.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-11-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:15:59 -07:00
Peng Fan
3f5c1277a9 remoteproc: imx_dsp_rproc: Simplify IMX_RPROC_RESET_CONTROLLER switch case
Introduce imx_dsp_rproc_reset_ctr_{start, stop, detect_mode}() helper
functions for i.MX variants using IMX_RPROC_RESET_CONTROLLER to manage
remote processors.

Allows the removal of the IMX_RPROC_RESET_CONTROLLER switch-case blocks
from imx_dsp_rproc_[start,stop,detect_mode](), resulting in cleaner and
more maintainable code.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-10-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:14:59 -07:00
Peng Fan
d5eb4d512f remoteproc: imx_dsp_rproc: Simplify IMX_RPROC_SCU_API switch case
Introduce imx_dsp_rproc_scu_api_{start, stop, detect_mode}() helper
functions for i.MX variants using IMX_RPROC_SCU_API to manage remote
processors.

Allows the removal of the IMX_RPROC_SCU_API switch-case blocks from
imx_dsp_rproc_[start,stop,detect_mode](), resulting in cleaner and more
maintainable code.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-9-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:13:58 -07:00
Peng Fan
9f356d1277 remoteproc: imx_dsp_rproc: Simplify IMX_RPROC_MMIO switch case
Introduce imx_dsp_rproc_mmio_{start, stop, detect_mode}() helper functions
for i.MX variants using IMX_RPROC_MMIO to manage remote processors.

Allows the removal of the IMX_RPROC_MMIO switch-case blocks from
imx_dsp_rproc_[start,stop,detect_mode](), resulting in cleaner and more
maintainable code.

No functional changes.

Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-8-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:12:46 -07:00
Peng Fan
66395eac5d remoteproc: imx_dsp_rproc: Move imx_dsp_rproc_dcfg closer to imx_dsp_rproc_of_match
Move the imx_dsp_rproc_dcfg structure definitions closer to
imx_dsp_rproc_of_match to prepare for adding start/stop/detect_mode ops for
each i.MX variant.

Avoids the need to declare function prototypes such as
'static int imx_dsp_rproc_mbox_init(struct imx_dsp_rproc *priv)' at the
beginning of the file, improving code organization and readability.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-7-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:11:52 -07:00
Peng Fan
606e481169 remoteproc: imx_dsp_rproc: Use start/stop/detect_mode ops from imx_rproc_dcfg
Allow each platform to provide its own implementation of start/stop/
detect_mode operations, and prepare to eliminate the need for multiple
switch-case statements.

Improve code readability and maintainability by encapsulating
platform-specific behavior.

No functional changes.

Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-6-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:10:32 -07:00
Peng Fan
8049dc7b63 remoteproc: imx_dsp_rproc: Drop extra space
Drop extra space between return and zero.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-5-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:09:21 -07:00
Peng Fan
e819a62d52 remoteproc: imx_dsp_rproc: Use dev_err_probe() for firmware and mode errors
Use dev_err_probe() to simplify the code. No functional change.

Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-4-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:08:18 -07:00
Peng Fan
4120602423 remoteproc: imx_dsp_rproc: Use devm_pm_runtime_enable() helper
Current code on the cleanup path just disables runtime PM for a device.

Using resource managed version devm_pm_runtime_enable() registers a cleanup
callback that sets autosuspend to false and then disables runtime PM for
a device. So, basically the same functionality as we don't use autosuspend
anyway.

As a result, the .remove callback is no longer needed, reducing boilerplate
code.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-3-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:07:03 -07:00
Peng Fan
36951036a7 remoteproc: imx_dsp_rproc: Use devm_rproc_add() helper
Replace manual rproc_add() and cleanup logic with devm_rproc_add(), which
ties the remoteproc lifecycle to the device's lifecycle. This simplifies
error handling and ensures proper cleanup.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-2-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:06:05 -07:00
Peng Fan
3003773ad6 remoteproc: imx_dsp_rproc: Simplify power domain attach and error handling
Refactor imx_dsp_attach_pm_domains() to use devm_pm_domain_attach_list()
directly, removing manual detach logic and simplifying resource management.

Also replace verbose error handling in imx_dsp_rproc_probe() with
dev_err_probe() for cleaner and more consistent error reporting.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Tested-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251119-imx-dsp-2025-11-19-v4-1-adafd342d07b@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-19 09:04:53 -07:00
Eric Biggers
78cd170d03 nfsd: Use MD5 library instead of crypto_shash
Update NFSD's support for "legacy client tracking" (which uses MD5) to
use the MD5 library instead of crypto_shash.  This has several benefits:

- Simpler code.  Notably, much of the error-handling code is no longer
  needed, since the library functions can't fail.

- Improved performance due to reduced overhead.  A microbenchmark of
  nfs4_make_rec_clidname() shows a speedup from 1455 cycles to 425.

- The MD5 code can now safely be built as a loadable module when nfsd is
  built as a loadable module.  (Previously, nfsd forced the MD5 code to
  built-in, presumably to work around the unreliability of the
  name-based loading.)  Thus select MD5 from the tristate option NFSD if
  NFSD_LEGACY_CLIENT_TRACKING, instead of from the bool option NFSD_V4.

- Fixes a bug where legacy client tracking was not supported on kernels
  booted with "fips=1", due to crypto_shash not allowing MD5 to be used.
  This particular use of MD5 is not for a cryptographic purpose, though,
  so it is acceptable even when fips=1 (see
  https://lore.kernel.org/r/dae495a93cbcc482f4ca23c3a0d9360a1fd8c3a8.camel@redhat.com/).

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-17 08:46:12 -05:00
NeilBrown
fceb8734e7 nfsd: stop pretending that we cache the SEQUENCE reply.
nfsd does not cache the reply to a SEQUENCE.  As the comment above
nfsd4_replay_cache_entry() says:

 * The sequence operation is not cached because we can use the slot and
 * session values.

The comment above nfsd4_cache_this() suggests otherwise.

 * The session reply cache only needs to cache replies that the client
 * actually asked us to.  But it's almost free for us to cache compounds
 * consisting of only a SEQUENCE op, so we may as well cache those too.
 * Also, the protocol doesn't give us a convenient response in the case
 * of a replay of a solo SEQUENCE op that wasn't cached

The code in nfsd4_store_cache_entry() makes it clear that only responses
beyond 'cstate.data_offset' are actually cached, and data_offset is set
at the end of nfsd4_encode_sequence() *after* the sequence response has
been encoded.

This patch simplifies code and removes the confusing comments.

- nfsd4_is_solo_sequence() is discarded as not-useful.
- nfsd4_cache_this() is now trivial so it too is discarded with the
  code placed in-line at the one call-site in nfsd4_store_cache_entry().
- nfsd4_enc_sequence_replay() is open-coded in to
  nfsd4_replay_cache_entry(), and then simplified to (hopefully) make
  the process of replaying a reply clearer.

Signed-off-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-17 08:46:12 -05:00
Bagas Sanjaya
8320b75b2b NFS: nfsd-maintainer-entry-profile: Inline function name prefixes
Sphinx reports htmldocs warnings:

Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst:185: ERROR: Unknown target name: "nfsd". [docutils]
Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst:188: ERROR: Unknown target name: "nfsdn". [docutils]
Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst:192: ERROR: Unknown target name: "nfsd4m". [docutils]

These are due to Sphinx confusing function name prefixes for external
link syntax. Fix the warnings by inlining the prefixes.

Fixes: 3a1ce35030 ("NFSD: Add a subsystem policy document")
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Closes: https://lore.kernel.org/linux-next/20251117174218.29365f30@canb.auug.org.au/
Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-17 08:46:06 -05:00
Thorsten Blum
a73d4a0556 drivers/xen/xenbus: Replace deprecated strcpy in xenbus_transaction_end
strcpy() is deprecated; inline the read-only string instead. Fix the
function comment and use bool instead of int while we're at it.

Link: https://github.com/KSPP/linux/issues/88
Reviewed-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Signed-off-by: Juergen Gross <jgross@suse.com>
Message-ID: <20251031112145.103257-2-thorsten.blum@linux.dev>
2025-11-17 08:48:40 +01:00
Thorsten Blum
6fec913ff1 drivers/xen/xenbus: Simplify return statement in join()
Don't unnecessarily negate 'buffer' and simplify the return statement.

Reviewed-by: Jason Andryuk <jason.andryuk@amd.com>
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Signed-off-by: Juergen Gross <jgross@suse.com>
Message-ID: <20251112171410.3140-2-thorsten.blum@linux.dev>
2025-11-17 07:55:10 +01:00
Chuck Lever
3a1ce35030 NFSD: Add a subsystem policy document
Steer contributors to NFSD's patchworks instance, list our patch
submission preferences, and more. The new document is based on the
existing netdev and xfs subsystem policy documents.

This is an attempt to add transparency to the process of accepting
contributions to NFSD and getting them merged upstream.

Suggested-by: "Darrick J. Wong" <djwong@kernel.org>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: NeilBrown <neil@brown.name>
[ cel: Hand-edits to address review comments ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Jeff Layton
6b3b697d65 sunrpc: allocate a separate bvec array for socket sends
svc_tcp_sendmsg() calls xdr_buf_to_bvec() with the second slot of
rq_bvec as the start, but doesn't reduce the array length by one, which
could lead to an array overrun. Also, rq_bvec is always rq_maxpages in
length, which can be too short in some cases, since the TCP record
marker consumes a slot.

Fix both problems by adding a separate bvec array to the svc_sock that
is specifically for sending. For TCP, make this array one slot longer
than rq_maxpages, to account for the record marker. For UDP, only
allocate as large an array as we need since it's limited to 64k of
payload.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
ebd3330d1c SUNRPC: Improve "fragment too large" warning
Including the client IP address that generated the overrun traffic
seems like it would be helpful. The message now reads:

  kernel: svc: nfsd oversized RPC fragment (1064958 octets) from 100.64.0.11:45866

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
d686e64e93 NFSD: Implement NFSD_IO_DIRECT for NFS READ
Add an experimental option that forces NFS READ operations to use
direct I/O instead of reading through the NFS server's page cache.

There is already at least one other layer of read caching: the page
cache on NFS clients.

The server's page cache, in many cases, is unlikely to provide
additional benefit. Some benchmarks have demonstrated that the
server's page cache is actively detrimental for workloads whose
working set is larger than the server's available physical memory.

For instance, on small NFS servers, cached NFS file content can
squeeze out local memory consumers. For large sequential workloads,
an enormous amount of data flows into and out of the page cache
and is consumed by NFS clients exactly once -- caching that data
is expensive to do and totally valueless.

For now this is a hidden option that can be enabled on test
systems for benchmarking. In the longer term, this option might
be enabled persistently or per-export. When the exported file
system does not support direct I/O, NFSD falls back to using
either DONTCACHE or buffered I/O to fulfill NFS READ requests.

Suggested-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
d7de37d6d7 NFSD: Relocate the xdr_reserve_space_vec() call site
In order to detect when a direct READ is possible, we need the send
buffer's .page_len to be zero when there is nothing in the buffer's
.pages array yet.

However, when xdr_reserve_space_vec() extends the size of the
xdr_stream to accommodate a READ payload, it adds to the send
buffer's .page_len.

It should be safe to reserve the stream space /after/ the VFS read
operation completes. This is, for example, how an NFSv3 READ works:
the VFS read goes into the rq_bvec, and is then added to the send
xdr_stream later by svcxdr_encode_opaque_pages().

Now that xdr_reserve_space_vec() uses the number of bytes actually
read, the xdr_truncate_encode() call is no longer necessary.

Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Mike Snitzer
803bc849f0 NFSD: pass nfsd_file to nfsd_iter_read()
Prepare for nfsd_iter_read() to use the DIO alignment stored in
nfsd_file by passing the nfsd_file to nfsd_iter_read() rather than
just the file which is associaed with the nfsd_file.

This means nfsd4_encode_readv() now also needs the nfsd_file rather
than the file.  Instead of changing the file arg to be the nfsd_file,
we discard the file arg as the nfsd_file (and indeed the file) is
already available via the "read" argument.

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Sergey Bashirov
cc6c40e09d NFSD/blocklayout: Support multiple extents per LAYOUTGET
Allow the pNFS server to respond with multiple extents to a LAYOUTGET
request, thereby avoiding unnecessary load on the server and improving
performance for the client. The number of LAYOUTGET requests is
significantly reduced for various file access patterns, including
random and parallel writes.

Additionally, this change allows the client to request layouts with the
loga_minlength value greater than the minimum possible length of a single
extent in XFS. We use this functionality to fix a livelock in the client.

Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Sergey Bashirov
0cd0d15d47 NFSD/blocklayout: Introduce layout content structure
Add a layout content structure instead of a single extent. The ability
to store and encode an array of extents is then used to implement support
for multiple extents per LAYOUTGET.

Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Sergey Bashirov
a1dce715c6 NFSD/blocklayout: Extract extent mapping from proc_layoutget
No changes in functionality. Split the proc_layoutget function to
create a helper function that maps single extent to the requested
range. This helper function is then used to implement support for
multiple extents per LAYOUTGET.

Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Sergey Bashirov
3524b021b0 NFSD/blocklayout: Fix minlength check in proc_layoutget
The extent returned by the file system may have a smaller offset than
the segment offset requested by the client. In this case, the minimum
segment length must be checked against the requested range. Otherwise,
the client may not be able to continue the read/write operation.

Fixes: 8650b8a058 ("nfsd: pNFS block layout driver")
Signed-off-by: Sergey Bashirov <sergeybashirov@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
566a414558 svcrdma: Increase the server's default RPC/RDMA credit grant
The range of commits from commit e3274026e2 ("SUNRPC: move all of
xprt handling into svc_xprt_handle()") to commit 15d39883ee
("SUNRPC: change the back-channel queue to lwq") enabled NFSD
performance to scale better as the number of nfsd threads is
increased. These commits were merged in v6.7.

Now that the nfsd thread count can scale to more threads, permit
individual clients to make more use of those threads. Increase the
RPC/RDMA per-connection credit grant from 64 to 128 -- same as the
Linux NFS client.

Simple single client fio-based benchmarking so far shows only
improvement, no regression.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
166274a245 NFSD: Update comment documenting unsupported fattr4 attributes
TIME_CREATE has been supported since commit e377a3e698 ("nfsd: Add
support for the birth time attribute").

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Matvey Kovalev
bfce8e4273 nfsd: delete unreachable confusing code in nfs4_open_delegation()
op_delegate_type is assigned OPEN_DELEGATE_NONE just before the if-block
where condition specifies it not be equal to OPEN_DELEGATE_NONE. Compiler
treats the block as unreachable and optimizes it out from the resulting
executable.

In that aspect commit d08d32e6e5 ("nfsd4: return delegation immediately
if lease fails") notably makes no difference.

Seems it's better to just drop this code instead of fiddling with memory
barriers or atomics.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

Signed-off-by: Matvey Kovalev <matvey.kovalev@ispras.ru>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
ccd608e29b NFSD: Add array bounds-checking in nfsd_iter_read()
The *count parameter does not appear to be explicitly restricted
to being smaller than rsize, so it might be possible to overrun
the rq_bvec or rq_pages arrays.

Rather than overrunning these arrays (damage done!) and then WARNING
once, let's harden the loop so that it terminates before the end of
the arrays are reached. This should result in a short read, which is
OK -- clients recover by sending additional READ requests for the
remaining unread bytes.

Reported-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Jeff Layton
b5fc406bc7 nfsd: switch the default for NFSD_LEGACY_CLIENT_TRACKING to "n"
We added this Kconfig option a little over a year ago. Switch the
default to "n" in preparation for its eventual removal.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
NeilBrown
4552f4e3f2 nfsd: change nfs4_client_to_reclaim() to allocate data
The calling convention for nfs4_client_to_reclaim() is clumsy in that
the caller needs to free memory if the function fails.  It is much
cleaner if the function frees its own memory.

This patch changes nfs4_client_to_reclaim() to re-allocate the .data
fields to be stored in the newly allocated struct nfs4_client_reclaim,
and to free everything on failure.

__cld_pipe_inprogress_downcall() needs to allocate the data anyway to
copy it from user-space, so now that data is allocated twice.  I think
that is a small price to pay for a cleaner interface.

Signed-off-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
NeilBrown
89bd77cf43 nfsd: move name lookup out of nfsd4_list_rec_dir()
nfsd4_list_rec_dir() is called with two different callbacks.
One of the callbacks uses vfs_rmdir() to remove the directory.
The other doesn't use the dentry at all, just the name.

As only one callback needs the dentry, this patch moves the lookup into
that callback.  This prepares of changes to how directory operations
are locked.

Signed-off-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Chuck Lever
bf94dea7fd svcrdma: Release transport resources synchronously
NFSD has always supported added network listeners. The new netlink
protocol now enables the removal of listeners.

Olga noticed that if an RDMA listener is removed and immediately
re-added, the deferred __svc_rdma_free() function might not have
run yet, so some or all of the old listener's RDMA resources
linger, which prevents a new listener on the same address from
being created.

Also, svc_xprt_free() does a module_put() just after calling
->xpo_free(). That means if there is deferred work going on, the
module could be unloaded before that work is even started,
resulting in a UAF.

Neil asks:
> What particular part of __svc_rdma_free() needs to run in order for a
> subsequent registration to succeed?
> Can that bit be run directory from svc_rdma_free() rather than be
> delayed?
> (I know almost nothing about rdma so forgive me if the answers to these
> questions seems obvious)

The reasons I can recall are:

 - Some of the transport tear-down work can sleep
 - Releasing a cm_id is tricky and can deadlock

We might be able to mitigate the second issue with judicious
application of transport reference counting.

Reported-by: Olga Kornievskaia <okorniev@redhat.com>
Closes: https://lore.kernel.org/linux-nfs/20250821204328.89218-1-okorniev@redhat.com/
Suggested-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
2025-11-16 18:20:11 -05:00
Haotian Zhang
5bcc5786a0 watchdog: starfive: Fix resource leak in probe error path
If pm_runtime_put_sync() fails after watchdog_register_device()
succeeds, the probe function jumps to err_exit without
unregistering the watchdog device. This leaves the watchdog
registered in the subsystem while the driver fails to load,
resulting in a resource leak.

Add a new error label err_unregister_wdt to properly unregister
the watchdog device.

Fixes: 8bc22a2f1b ("watchdog: starfive: Check pm_runtime_enabled() before decrementing usage counter")
Signed-off-by: Haotian Zhang <vulab@iscas.ac.cn>
Reviewed-by: Wim Van Sebroeck <wim@linux-watchdog.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:29:01 +01:00
Christian Marangi
6fbf541520 dt-bindings: watchdog: airoha: Add support for Airoha AN7583 SoC
Add compatible for Airoha AN7583 SoC. The implementation is exactly the
same of Airoha EN7581 hence we add the compatible in addition to EN7581
ones.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:12 +01:00
Aleksander Jan Bajkowski
26f2f5ed16 dt-bindings: watchdog: lantiq,wdt: convert bindings to dtschema
Convert the Lantiq WDT Watchdog bindings to yaml format.

Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:12 +01:00
Heiko Stuebner
a8c762cbd1 dt-bindings: watchdog: Add RK3506 compatible
The watchdog used on the RK3506 is still the same snps,dw-wdt compatible
one that is in use since the RK3066 days, so add the RK3506 to the
variant list.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:12 +01:00
Jingyi Wang
aa33a6c8ce dt-bindings: watchdog: Document Qualcomm Kaanapali watchdog
Add devicetree binding for watchdog present on Qualcomm Kaanapali SoC.

Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:11 +01:00
Haotian Zhang
25c0b472ea watchdog: wdat_wdt: Fix ACPI table leak in probe function
wdat_wdt_probe() calls acpi_get_table() to obtain the WDAT ACPI table but
never calls acpi_put_table() on any paths. This causes a permanent ACPI
table memory leak.

Add a single cleanup path which calls acpi_put_table() to ensure
the ACPI table is always released.

Fixes: 058dfc7670 ("ACPI / watchdog: Add support for WDAT hardware watchdog")
Suggested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Haotian Zhang <vulab@iscas.ac.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:10 +01:00
Binbin Zhou
e0c50cddbd watchdog: loongson1: Add Loongson-2k0300 watchdog support
According to the manual, the Loongson-2K0300 watchdog is similar to the
Loongson-1, except for some register offsets and inconsistent register
bit definitions. Separate definitions via driver_data suffice.

Co-developed-by: Xiaochuang Mao <maoxiaochuan@loongson.cn>
Signed-off-by: Xiaochuang Mao <maoxiaochuan@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:10 +01:00
Binbin Zhou
e4948e8011 dt-bindings: watchdog: loongson,ls1x-wdt: Add ls2k0300-wdt compatible
Add "loongson,ls2k0300-wdt" compatible to the dt-schema document, which
is similar to Loongson-1 watchdog, but with differences in some register
offsets and bit definitions.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:10 +01:00
Binbin Zhou
9d8ca99d60 watchdog: loongson1: Drop CONFIG_OF
The general recommendation is to not use of_match_ptr() or CONFIG_OF
ifdef.

Drop them.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:09 +01:00
Binbin Zhou
6121d0b889 watchdog: loongson1: Simplify ls1x_wdt_probe code
Remove meaningless output to simplify ls1x_wdt_probe().

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:09 +01:00
Binbin Zhou
f909b3d4f1 watchdog: loongson1: Add missing MODULE_PARM_DESC
Add documentation for module_param so that they're visible with
modinfo command.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:08 +01:00
Zoe Gates
f0a4bf61f1 watchdog/diag288: Fix module comment typos
Correct spelling and capitalizaion in the header comment so the
documentation reads cleanly.

Signed-off-by: Zoe Gates <zoe@zeocities.dev>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:28:08 +01:00
Jack Hsu
a742d1713c dt-bindings: watchdog: Support MediaTek MT8189 wdt
modify dt-binding for support mt8189 dts node of wdt

Signed-off-by: Jack Hsu <jh.hsu@mediatek.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:24:29 +01:00
Louis-Alexis Eyraud
26d21c835f dt-bindings: watchdog: mediatek,mtk-wdt: Add compatible for MT8189 SoC
Add compatible string for the watchdog block on MT8189 SoC, which is
compatible with the one used on MT6589.

Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:57 +01:00
Krzysztof Kozlowski
550d1bda39 dt-bindings: mfd: rohm,bd96801-pmic: Correct timeout-sec length and reference watchdog schema
The parent node of ROHM BD96801 PMIC is also holding properties for the
watchdog, thus it should reference watchdog.yaml schema.  OTOH, the
timeout-sec property is used only as one number.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Acked-by: Matti Vaittinen <mazziesaccount@gmail.com>
Acked-by: Lee Jones <lee@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:56 +01:00
Krzysztof Kozlowski
017bca9163 dt-bindings: watchdog: Allow node names named 'pmic'
Watchdog is often part of more complex devices like Power Management ICs
(PMIC), e.g. on rohm,bd96801, and the schema can be referenced by a
binding describing parent (main) node.  Allow another typical name for
such PMIC devices: pmic.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:56 +01:00
Krzysztof Kozlowski
0917135963 dt-bindings: watchdog: Restrict timeout-sec to one number
Linux kernel expects only one number for the watchdog timeout and the
type is an array (defined in property-units.yaml in DT schema), so
restrict the property.

Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:55 +01:00
Wolfram Sang
babe81b061 watchdog: renesas_wwdt: add driver
This driver adds support for the Renesas Window Watchdog Timer (WWDT).
Because it can only be setup once after boot and we cannot know if this
already happened in early boot stages, it is mandated that the firmware
configures the watchdog. Linux then adapts according to the given
setup. Note that this watchdog only reports an overflow to the Error
Control Module (ECM) and does not reset the SoC on its own.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:55 +01:00
Wolfram Sang
ece1ad19c3 dt-bindings: watchdog: Add Renesas WWDT
Describe the Window Watchdog Timer found on Renesas R-Car SoCs from late
Gen3 onwards.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:54 +01:00
Rob Herring (Arm)
fbd10d9670 dt-bindings: watchdog: Convert marvell,orion-wdt to DT schema
Convert the Marvell Orion and Armada watchdog binding to DT schema
format. It's a straight-forward conversion.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Gregory CLEMENT <gregory.clement@bootlin.com>
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:54 +01:00
Rob Herring (Arm)
af34a25336 dt-bindings: watchdog: Convert TI OMAP to DT schema
Convert the TI OMAP watchdog binding to DT schema format. The compatible
string list was incomplete. The "reg" and "interrupts" properties were
missing. "ti,hwmods" is also deprecated and not required.

Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:53 +01:00
Chin-Ting Kuo
13e8664671 watchdog: aspeed: Add support for AST2700 platform
Add AST2700 platform support to the ASPEED watchdog driver. This includes
a new per-platform configuration with SCU reset status register at
SCU1_070 and support for 5 reset mask registers.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:53 +01:00
Chin-Ting Kuo
0eb54296dc watchdog: aspeed: Support variable number of reset mask registers
Starting from the AST2600 platform, the SoC design has become more
complex, with an increased number of reset mask registers.
To support this, introduce a new field 'num_reset_masks' in the
'aspeed_wdt_config' structure to specify the number of reset mask
registers per platform. This change removes the need for hardcoded
platform-specific logic and improves scalability for future SoCs.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:52 +01:00
Chin-Ting Kuo
b3bc229b54 dt-bindings: watchdog: aspeed,ast2400-wdt: Add support for AST2700
Add support for the AST2700 SoC in the ASPEED watchdog device tree
bindings. This includes:

- Adding "aspeed,ast2700-wdt" to the compatible string list.
- Extending the "aspeed,reset-mask" property description for AST2700.
- Defining AST2700-specific reset mask bits in aspeed-wdt.h,
  covering RESET1 to RESET5.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:52 +01:00
Wolfram Sang
1cafd2a850 dt-bindings: watchdog: renesas,wdt: add SWDT exception for V3H
The SWDT on V3H has no reset bit. Make resets optional on this SoC.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:51 +01:00
Wolfram Sang
fcba285525 dt-bindings: watchdog: factor out RZ/V2H(P) watchdog
Renesas created different watchdog IPs but they are all handled in the
same binding documentation. This leads to a lot of conditional handling
which makes it unnecessarily hard to add new items. Factor out the
RZ/V2H(P) watchdog to make handling easier.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:51 +01:00
Wolfram Sang
909c8ea1ad dt-bindings: watchdog: factor out RZ/G2L watchdog
Renesas created different watchdog IPs but they are all handled in the
same binding documentation. This leads to a lot of conditional handling
which makes it unnecessarily hard to add new items. Factor out the
RZ/G2L watchdog to make handling easier.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:50 +01:00
Wolfram Sang
a3e32b41c2 dt-bindings: watchdog: factor out RZ/N1 watchdog
Renesas created different watchdog IPs but they are all handled in the
same binding documentation. This leads to a lot of conditional handling
which makes it unnecessarily hard to add new items. Factor out the RZ/N1
watchdog to make handling easier.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:50 +01:00
Wolfram Sang
055f0576e8 dt-bindings: watchdog: factor out RZ/A watchdog
Renesas created different watchdog IPs but they are all handled in the
same binding documentation. This leads to a lot of conditional handling
which makes it unnecessarily hard to add new items. Factor out the RZ/A
watchdog to make handling easier.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:49 +01:00
Li Qiang
7aa31ee9ec via_wdt: fix critical boot hang due to unnamed resource allocation
The VIA watchdog driver uses allocate_resource() to reserve a MMIO
region for the watchdog control register. However, the allocated
resource was not given a name, which causes the kernel resource tree
to contain an entry marked as "<BAD>" under /proc/iomem on x86
platforms.

During boot, this unnamed resource can lead to a critical hang because
subsequent resource lookups and conflict checks fail to handle the
invalid entry properly.

Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2025-11-15 15:19:48 +01:00
Dan Williams
f7ae6d4ec6 PCI/TSM: Add 'dsm' and 'bound' attributes for dependent functions
PCI/TSM sysfs for physical function 0 devices, i.e. the "DSM" (Device
Security Manager), contains the 'connect' and 'disconnect' attributes.
After a successful 'connect' operation the DSM, its dependent functions
(SR-IOV virtual functions, non-zero multi-functions, or downstream
endpoints of a switch DSM) are candidates for being transitioned into a
TDISP (TEE Device Interface Security Protocol) operational state, via
pci_tsm_bind(). At present sysfs is blind to which devices are capable of
TDISP operation and it is ambiguous which functions are serviced by which
DSMs.

Add a 'dsm' attribute to identify a function's DSM device, and add a
'bound' attribute to identify when a function has entered a TDISP
operational state.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-9-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-14 15:06:57 -08:00
Dan Williams
c316c75d57 PCI/TSM: Add pci_tsm_guest_req() for managing TDIs
A PCIe device function interface assigned to a TVM is a TEE Device
Interface (TDI). A TDI instantiated by pci_tsm_bind() needs additional
steps taken by the TVM to be accepted into the TVM's Trusted Compute
Boundary (TCB) and transitioned to the RUN state.

pci_tsm_guest_req() is a channel for the guest to request TDISP collateral,
like Device Interface Reports, and effect TDISP state changes, like
LOCKED->RUN transititions. Similar to IDE establishment and pci_tsm_bind(),
these are long running operations involving SPDM message passing via the
DOE mailbox.

The path for a TVM to invoke pci_tsm_guest_req() is:
* TSM triggers exit via guest-to-host-interface ABI (implementation specific)
* VMM invokes handler (KVM handle_exit() -> userspace io)
* handler issues request (userspace io handler -> ioctl() ->
  pci_tsm_guest_req())
* handler supplies response
* VMM posts response, notifies/re-enters TVM

This path is purely a transport for messages from TVM to platform TSM. By
design the host kernel does not and must not care about the content of
these messages. I.e. the host kernel is not in the TCB of the TVM.

As this is an opaque passthrough interface, similar to fwctl, the kernel
requires that implementations stay within the bounds defined by 'enum
pci_tsm_req_scope'. Violation of those expectations likely has market and
regulatory consequences. Out of scope requests are blocked by default.

Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-8-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-14 15:06:57 -08:00
Dan Williams
50cbec192f PCI/TSM: Add pci_tsm_bind() helper for instantiating TDIs
After a PCIe device has established a secure link and session between a TEE
Security Manager (TSM) and its local Device Security Manager (DSM), the
device or its subfunctions are candidates to be bound to a private memory
context, a TVM. A PCIe device function interface assigned to a TVM is a TEE
Device Interface (TDI).

The pci_tsm_bind() requests the low-level TSM driver to associate the
device with private MMIO and private IOMMU context resources of a given TVM
represented by a @kvm argument. A device in the bound state corresponds to
the TDISP protocol LOCKED state and awaits validation by the TVM. It is a
'struct pci_tsm_link_ops' operation because, similar to IDE establishment,
it involves host side resource establishment and context setup on behalf of
the guest. It is also expected to be performed lazily to allow for
operation of the device in non-confidential "shared" context for pre-lock
configuration.

Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-7-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-14 15:06:57 -08:00
Dan Williams
079115370d PCI/IDE: Initialize an ID for all IDE streams
The PCIe spec defines two types of streams - selective and link.  Each
stream has an ID from the same bucket so a stream ID does not tell the
type.  The spec defines an "enable" bit for every stream and required
stream IDs to be unique among all enabled stream but there is no such
requirement for disabled streams.

However, when IDE_KM is programming keys, an IDE-capable device needs
to know the type of stream being programmed to write it directly to
the hardware as keys are relatively large, possibly many of them and
devices often struggle with keeping around rather big data not being
used.

Walk through all streams on a device and initialise the IDs to some
unique number, both link and selective.

The weakest part of this proposal is the host bridge ide_stream_ids_ida.
Technically, a Stream ID only needs to be unique within a given partner
pair. However, with "anonymous" / unassigned streams there is no convenient
place to track the available ids. Proceed with an ida in the host bridge
for now, but consider moving this tracking to be an ide_stream_ids_ida per
device.

Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-6-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-14 15:06:57 -08:00
Xu Yilun
f86e51399c PCI/IDE: Add Address Association Register setup for downstream MMIO
The address ranges for downstream Address Association Registers need to
cover memory addresses for all functions (PFs/VFs/downstream devices)
managed by a Device Security Manager (DSM). The proposed solution is get
the memory (32-bit only) range and prefetchable-memory (64-bit capable)
range from the immediate ancestor downstream port (either the direct-attach
RP or deepest switch port when switch attached).

Similar to RID association, address associations will be set by default if
hardware sets 'Number of Address Association Register Blocks' in the
'Selective IDE Stream Capability Register' to a non-zero value. TSM drivers
can opt-out of the settings by zero'ing out unwanted / unsupported address
ranges. E.g. TDX Connect only supports prefetachable (64-bit capable)
memory ranges for the Address Association setting.

If the immediate downstream port provides both a memory range and
prefetchable-memory range, but the IDE partner port only provides 1 Address
Association Register block then the TSM driver can pick which range to
associate, or let the PCI core prioritize memory.

Note, the Address Association Register setup for upstream requests is still
uncertain so is not included.

Co-developed-by: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Co-developed-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Co-developed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251114010227.567693-1-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-14 15:06:57 -08:00
Dan Williams
c16af019d9 resource: Introduce resource_assigned() for discerning active resources
A PCI bridge resource lifecycle involves both a "request" and "assign"
phase. At any point in time that resource may not yet be assigned, or may
have failed to assign (because it does not fit).

There are multiple conventions to determine when assignment has not
completed: IORESOURCE_UNSET, IORESOURCE_DISABLED, and checking whether the
resource is parented.

In code paths that are known to not be racing assignment, e.g. post
subsys_initcall(), the most reliable method to judge that a bridge resource
is assigned is to check the resource is parented [1].

Introduce a resource_assigned() helper for this purpose.

Link: http://lore.kernel.org/2b9f7f7b-d6a4-be59-14d4-7b4ffccfe373@linux.intel.com [1]
Suggested-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-4-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-13 17:05:08 -08:00
Dan Williams
e5b5f8b7c2 PCI/TSM: Drop stub for pci_tsm_doe_transfer()
Just like pci_tsm_pf0_{con,de}structor(), in the CONFIG_PCI_TSM=n case there
should be no callers of pci_tsm_doe_transfer().

Reported-by: Xu Yilun <yilun.xu@linux.intel.com>
Closes: http://lore.kernel.org/aRFfk14DJWEVhC/R@yilunxu-OptiPlex-7050
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-3-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-13 17:05:08 -08:00
Dan Williams
110c155e8a drivers/virt: Drop VIRT_DRIVERS build dependency
All of the objects in drivers/virt/ have their own configuration symbols to
gate compilation. I.e. nothing gets added to the kernel with
CONFIG_VIRT_DRIVERS=y in isolation.

Unconditionally descend into drivers/virt/ so that consumers do not need to
add an additional CONFIG_VIRT_DRIVERS dependency.

Fix warnings of the form:

    Kconfig warnings: (for reference only)
       WARNING: unmet direct dependencies detected for TSM
       Depends on [n]: VIRT_DRIVERS [=n]
       Selected by [y]:
       - PCI_TSM [=y] && PCI [=y]

...where PCI_TSM selects CONFIG_TSM, but fails to select
CONFIG_VIRT_DRIVERS.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202511041832.ylcgIiqN-lkp@intel.com/
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251113021446.436830-2-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-13 17:05:08 -08:00
Dan Carpenter
5a3d530caa remoteproc: mediatek: Change the snprintf() checking
The snprintf() calls here work but they have several minor style issues:

1) It uses ARRAY_SIZE() which is the number of elements in an array.
   Since were talking about char that works, but it's more common to
   use sizeof() which is the number of bytes.
2) The printf format is "%1d".  The "1" ensures we always print at
   least 1 character but since numbers all have at least 1 digit this
   can be removed.
3) The kernel implementation of snprintf() cannot return negative error
   codes.  Also these particular calls to snprintf() can't return zero
   and the code to handle that zero return is sort of questionable.
4) In the current kernel the only "core_id" we print is "0" but if it
   was more than 9 then the output would be truncated so GCC complains.
   Add an "a >= sizeof(scp_fw_file)" check for output which is too long.

Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/aP8agyKj73bLZrTQ@stanley.mountain
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-11-10 10:40:44 -07:00
Aaron Tomlin
1ddac5cd7f MAINTAINERS: Add myself as reviewer for module support
Voluntering as a reviewer for Module support.

Suggested-by: Luis Chamberlain <mcgrof@kernel.org>
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Acked-by: Luis Chamberlain <mcgrof@kernel.org>
Acked-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
2025-11-10 16:20:35 +01:00
Bagas Sanjaya
acd9ea1714 Documentation: btt: Unwrap bit 31-30 nested table
Bit 31-30 usage table is already formatted as reST simple table, but it
is wrapped in literal code block instead. Unwrap it.

Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Link: https://patch.msgid.link/20251105124707.44736-2-bagasdotme@gmail.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2025-11-07 09:06:51 -06:00
Andy Shevchenko
6f15c3d715 bitops: Update kernel-doc in hweight.c to fix the issues with it
The kernel-doc in lib/hweight.c is global to  the file and
currently has issues:

Warning: lib/hweight.c:13 expecting prototype for hweightN(). Prototype was for __sw_hweight32() instead
Warning: lib/hweight.c:13 function parameter 'w' not described in '__sw_hweight32'

Update it accordingly.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-06 11:51:04 -05:00
Andy Shevchenko
0cb302c9c9 bitops: Add missed file to MAINTAINERS
In accordance with the history and nature of the operation
add lib/hweight.c to the BITOPS record in MAINTAINERS.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
2025-11-06 11:51:04 -05:00
Marco Crivellari
7e898a9a99 nvdimm: replace use of system_wq with system_percpu_wq
Currently if a user enqueues a work item using schedule_delayed_work() the
used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use
WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to
schedule_work() that is using system_wq and queue_work(), that makes use
again of WORK_CPU_UNBOUND.

This lack of consistency cannot be addressed without refactoring the API.

This patch continues the effort to refactor worqueue APIs, which has begun
with the change introducing new workqueues and a new alloc_workqueue flag:

commit 128ea9f6cc ("workqueue: Add system_percpu_wq and system_dfl_wq")
commit 930c2ea566 ("workqueue: Add new WQ_PERCPU flag")

Replace system_wq with system_percpu_wq, keeping the same old behavior.
The old wq (system_wq) will be kept for a few release cycles.

Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>> ---
Link: https://patch.msgid.link/20251105150826.248673-1-marco.crivellari@suse.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2025-11-05 15:48:11 -06:00
Kees Cook
ae83f3b726 module: Add compile-time check for embedded NUL characters
Long ago, the kernel module license checks were bypassed by embedding a
NUL character in the MODULE_LICENSE() string[1]. By using a string like
"GPL\0proprietary text", the kernel would only read "GPL" due to C string
termination at the NUL byte, allowing proprietary modules to avoid kernel
tainting and access GPL-only symbols.

The MODULE_INFO() macro stores these strings in the .modinfo ELF
section, and get_next_modinfo() uses strcmp()-family functions
which stop at the first NUL. This split the embedded string into two
separate .modinfo entries, with only the first part being processed by
license_is_gpl_compatible().

Add a compile-time check using static_assert that compares the full
string length (sizeof - 1) against __builtin_strlen(), which stops at
the first NUL. If they differ, compilation fails with a clear error
message.

While this check can still be circumvented by modifying the ELF binary
post-compilation, it prevents accidental embedded NULs and forces
intentional abuse to require deliberate binary manipulation rather than
simple source-level tricks.

Build tested with test modules containing both valid and invalid license
strings. The check correctly rejects:

    MODULE_LICENSE("GPL\0proprietary")

while accepting normal declarations:

    MODULE_LICENSE("GPL")

Link: https://lwn.net/Articles/82305/ [1]
Suggested-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Kees Cook <kees@kernel.org>
Reviewed-by: Daniel Gomez <da.gomez@samsung.com>
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
2025-11-05 14:08:58 +01:00
Kees Cook
57e9853737 media: radio: si470x: Fix DRIVER_AUTHOR macro definition
The DRIVER_AUTHOR macro incorrectly included a semicolon in its
string literal definition. Right now, this wasn't causing any real
problem, but coming changes to the MODULE_INFO() macro make this more
sensitive. Specifically, when used with MODULE_AUTHOR(), this created
syntax errors during macro expansion:

    MODULE_AUTHOR(DRIVER_AUTHOR);

expands to:

    MODULE_INFO(author, "Joonyoung Shim <jy0922.shim@samsung.com>";)
                                                                  ^
                                                       syntax error

Remove the trailing semicolon from the DRIVER_AUTHOR definition.
Semicolons should only appear at the point of use, not in the macro
definition.

Reviewed-by: Hans Verkuil <hverkuil+cisco@kernel.org>
Signed-off-by: Kees Cook <kees@kernel.org>
Reviewed-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Reviewed-by: Daniel Gomez <da.gomez@samsung.com>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
2025-11-05 14:08:56 +01:00
Kees Cook
9de2198ab9 media: dvb-usb-v2: lmedm04: Fix firmware macro definitions
The firmware filename macros incorrectly included semicolons in their
string literal definitions. Right now, this wasn't causing any real
problem, but coming changes to the MODULE_INFO() macro make this more
sensitive. Specifically, when used with MODULE_FIRMWARE(), this
created syntax errors during macro expansion:

    MODULE_FIRMWARE(LME2510_C_S7395);

expands to:

    MODULE_INFO(firmware, "dvb-usb-lme2510c-s7395.fw";)
                                                     ^
                                          syntax error

Remove the trailing semicolons from all six firmware filename macro
definitions. Semicolons should only appear at the point of use, not in
the macro definition.

Reviewed-by: Hans Verkuil <hverkuil+cisco@kernel.org>
Signed-off-by: Kees Cook <kees@kernel.org>
Reviewed-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Reviewed-by: Daniel Gomez <da.gomez@samsung.com>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@samsung.com>
2025-11-05 14:08:55 +01:00
Dan Williams
a4438f06b1 PCI/TSM: Report active IDE streams
Given that the platform TSM owns IDE Stream ID allocation, report the
active streams via the TSM class device. Establish a symlink from the
class device to the PCI endpoint device consuming the stream, named by
the Stream ID.

Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251031212902.2256310-10-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
9ddaf9c3ed PCI/IDE: Report available IDE streams
The limited number of link-encryption (IDE) streams that a given set of
host bridges supports is a platform specific detail. Provide
pci_ide_init_nr_streams() as a generic facility for either platform TSM
drivers, or PCI core native IDE, to report the number available streams.
After invoking pci_ide_init_nr_streams() an "available_secure_streams"
attribute appears in PCI host bridge sysfs to convey that count.

Introduce a device-type, @pci_host_bridge_type, now that both a release
method and sysfs attribute groups are being specified for all 'struct
pci_host_bridge' instances.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251031212902.2256310-9-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
1e4d2ff3ae PCI/IDE: Add IDE establishment helpers
There are two components to establishing an encrypted link, provisioning
the stream in Partner Port config-space, and programming the keys into
the link layer via IDE_KM (IDE Key Management). This new library,
drivers/pci/ide.c, enables the former. IDE_KM, via a TSM low-level
driver, is saved for later.

With the platform TSM implementations of SEV-TIO and TDX Connect in mind
this library abstracts small differences in those implementations. For
example, TDX Connect handles Root Port register setup while SEV-TIO
expects System Software to update the Root Port registers. This is the
rationale for fine-grained 'setup' + 'enable' verbs.

The other design detail for TSM-coordinated IDE establishment is that
the TSM may manage allocation of Stream IDs, this is why the Stream ID
value is passed in to pci_ide_stream_setup().

The flow is:

pci_ide_stream_alloc():
    Allocate a Selective IDE Stream Register Block in each Partner Port
    (Endpoint + Root Port), and reserve a host bridge / platform stream
    slot. Gather Partner Port specific stream settings like Requester ID.

pci_ide_stream_register():
    Publish the stream in sysfs after allocating a Stream ID. In the TSM
    case the TSM allocates the Stream ID for the Partner Port pair.

pci_ide_stream_setup():
    Program the stream settings to a Partner Port. Caller is responsible
    for optionally calling this for the Root Port as well if the TSM
    implementation requires it.

pci_ide_stream_enable():
    Enable the stream after IDE_KM.

In support of system administrators auditing where platform, Root Port,
and Endpoint IDE stream resources are being spent, the allocated stream
is reflected as a symlink from the host bridge to the endpoint with the
name:

    stream%d.%d.%d

Where the tuple of integers reflects the allocated platform, Root Port,
and Endpoint stream index (Selective IDE Stream Register Block) values.

Thanks to Wu Hao for a draft implementation of this infrastructure.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251031212902.2256310-8-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
290b633a7d PCI: Establish document for PCI host bridge sysfs attributes
In preparation for adding more host bridge sysfs attributes, document the
existing naming format and 'firmware_node' attribute.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251031212902.2256310-7-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
c0c1262fbf PCI: Add PCIe Device 3 Extended Capability enumeration
PCIe r7.0 Section 7.7.9 Device 3 Extended Capability Structure, defines the
canonical location for determining the Flit Mode of a device. This status
is a dependency for PCIe IDE enabling. Add a new fm_enabled flag to 'struct
pci_dev'.

Cc: Lukas Wunner <lukas@wunner.de>
Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251031212902.2256310-6-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
3225f52cde PCI/TSM: Establish Secure Sessions and Link Encryption
The PCIe 7.0 specification, section 11, defines the Trusted Execution
Environment (TEE) Device Interface Security Protocol (TDISP).  This
protocol definition builds upon Component Measurement and Authentication
(CMA), and link Integrity and Data Encryption (IDE). It adds support for
assigning devices (PCI physical or virtual function) to a confidential VM
such that the assigned device is enabled to access guest private memory
protected by technologies like Intel TDX, AMD SEV-SNP, RISCV COVE, or ARM
CCA.

The "TSM" (TEE Security Manager) is a concept in the TDISP specification
of an agent that mediates between a "DSM" (Device Security Manager) and
system software in both a VMM and a confidential VM. A VMM uses TSM ABIs
to setup link security and assign devices. A confidential VM uses TSM
ABIs to transition an assigned device into the TDISP "RUN" state and
validate its configuration. From a Linux perspective the TSM abstracts
many of the details of TDISP, IDE, and CMA. Some of those details leak
through at times, but for the most part TDISP is an internal
implementation detail of the TSM.

CONFIG_PCI_TSM adds an "authenticated" attribute and "tsm/" subdirectory
to pci-sysfs. Consider that the TSM driver may itself be a PCI driver.
Userspace can watch for the arrival of a "TSM" device,
/sys/class/tsm/tsm0/uevent KOBJ_CHANGE, to know when the PCI core has
initialized TSM services.

The operations that can be executed against a PCI device are split into
two mutually exclusive operation sets, "Link" and "Security" (struct
pci_tsm_{link,security}_ops). The "Link" operations manage physical link
security properties and communication with the device's Device Security
Manager firmware. These are the host side operations in TDISP. The
"Security" operations coordinate the security state of the assigned
virtual device (TDI). These are the guest side operations in TDISP.

Only "link" (Secure Session and physical Link Encryption) operations are
defined at this stage. There are placeholders for the device security
(Trusted Computing Base entry / exit) operations.

The locking allows for multiple devices to be executing commands
simultaneously, one outstanding command per-device and an rwsem
synchronizes the implementation relative to TSM registration/unregistration
events.

Thanks to Wu Hao for his work on an early draft of this support.

Cc: Lukas Wunner <lukas@wunner.de>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Link: https://patch.msgid.link/20251031212902.2256310-5-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:41 -08:00
Dan Williams
215afa89d2 PCI: Introduce pci_walk_bus_reverse(), for_each_pci_dev_reverse()
PCI/TSM, the PCI core functionality for the PCIe TEE Device Interface
Security Protocol (TDISP), has a need to walk all subordinate functions of
a Device Security Manager (DSM) to setup a device security context. A DSM
is physical function 0 of multi-function or SR-IOV device endpoint, or it
is an upstream switch port.

In error scenarios or when a TEE Security Manager (TSM) device is removed
it needs to unwind all established DSM contexts.

Introduce reverse versions of PCI device iteration helpers to mirror the
setup path and ensure that dependent children are handled before parents.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20251031212902.2256310-4-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:40 -08:00
Dan Williams
f16469ee73 PCI/IDE: Enumerate Selective Stream IDE capabilities
Link encryption is a new PCIe feature enumerated by "PCIe r7.0 section
7.9.26 IDE Extended Capability".

It is both a standalone port + endpoint capability, and a building block
for the security protocol defined by "PCIe r7.0 section 11 TEE Device
Interface Security Protocol (TDISP)". That protocol coordinates device
security setup between a platform TSM (TEE Security Manager) and a
device DSM (Device Security Manager). While the platform TSM can
allocate resources like Stream ID and manage keys, it still requires
system software to manage the IDE capability register block.

Add register definitions and basic enumeration in preparation for
Selective IDE Stream establishment. A follow on change selects the new
CONFIG_PCI_IDE symbol. Note that while the IDE specification defines
both a point-to-point "Link Stream" and a Root Port to endpoint
"Selective Stream", only "Selective Stream" is considered for Linux as
that is the predominant mode expected by Trusted Execution Environment
Security Managers (TSMs), and it is the security model that limits the
number of PCI components within the TCB in a PCIe topology with
switches.

Co-developed-by: Alexey Kardashevskiy <aik@amd.com>
Signed-off-by: Alexey Kardashevskiy <aik@amd.com>
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
Reviewed-by: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Link: https://patch.msgid.link/20251031212902.2256310-3-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:40 -08:00
Dan Williams
603c646f00 coco/tsm: Introduce a core device for TEE Security Managers
A "TSM" is a platform component that provides an API for securely
provisioning resources for a confidential guest (TVM) to consume. The
name originates from the PCI specification for platform agent that
carries out operations for PCIe TDISP (TEE Device Interface Security
Protocol).

Instances of this core device are parented by a device representing the
platform security function like CONFIG_CRYPTO_DEV_CCP or
CONFIG_INTEL_TDX_HOST.

This device interface is a frontend to the aspects of a TSM and TEE I/O
that are cross-architecture common. This includes mechanisms like
enumerating available platform TEE I/O capabilities and provisioning
connections between the platform TSM and device DSMs (Device Security
Manager (TDISP)).

For now this is just the scaffolding for registering a TSM device sysfs
interface.

Cc: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Co-developed-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
Link: https://patch.msgid.link/20251031212902.2256310-2-dan.j.williams@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2025-11-03 19:27:40 -08:00
Alison Schofield
f59b701b46 tools/testing/nvdimm: Use per-DIMM device handle
KASAN reports a global-out-of-bounds access when running these nfit
tests: clear.sh, pmem-errors.sh, pfn-meta-errors.sh, btt-errors.sh,
daxdev-errors.sh, and inject-error.sh.

[] BUG: KASAN: global-out-of-bounds in nfit_test_ctl+0x769f/0x7840 [nfit_test]
[] Read of size 4 at addr ffffffffc03ea01c by task ndctl/1215
[] The buggy address belongs to the variable:
[] handle+0x1c/0x1df4 [nfit_test]

nfit_test_search_spa() uses handle[nvdimm->id] to retrieve a device
handle and triggers a KASAN error when it reads past the end of the
handle array. It should not be indexing the handle array at all.

The correct device handle is stored in per-DIMM test data. Each DIMM
has a struct nfit_mem that embeds a struct acpi_nfit_memdev that
describes the NFIT device handle. Use that device handle here.

Fixes: 10246dc84d ("acpi nfit: nfit_test supports translate SPA")
Cc: stable@vger.kernel.org
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>> ---
Link: https://patch.msgid.link/20251031234227.1303113-1-alison.schofield@intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2025-11-03 16:47:13 -06:00
Mike Rapoport (Microsoft)
43bc0aa19a nvdimm: allow exposing RAM carveouts as NVDIMM DIMM devices
There are use cases, for example virtual machine hosts, that create
"persistent" memory regions using memmap= option on x86 or dummy
pmem-region device tree nodes on DT based systems.

Both these options are inflexible because they create static regions and
the layout of the "persistent" memory cannot be adjusted without reboot
and sometimes they even require firmware update.

Add a ramdax driver that allows creation of DIMM devices on top of
E820_TYPE_PRAM regions and devicetree pmem-region nodes.

The DIMMs support label space management on the "device" and provide a
flexible way to access RAM using fsdax and devdax.

Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Link: https://patch.msgid.link/20251026153841.752061-2-rppt@kernel.org
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2025-11-03 14:50:42 -06:00
Andreas Hindborg
ee3b8134b2 modules: add rust modules files to MAINTAINERS
The module subsystem people agreed to maintain rust support for modules
[1]. Thus, add entries for relevant files to modules entry in MAINTAINERS.

Link: https://lore.kernel.org/all/0d9e596a-5316-4e00-862b-fd77552ae4b5@suse.com/ [1]

Acked-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:43:42 +01:00
Andreas Hindborg
e119c2fe8c rust: samples: add a module parameter to the rust_minimal sample
Showcase the rust module parameter support by adding a module parameter to
the `rust_minimal` sample.

Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:42:34 +01:00
Andreas Hindborg
0b24f9740f rust: module: update the module macro with module parameter support
Allow module parameters to be declared in the rust `module!` macro.

Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:42:29 +01:00
Andreas Hindborg
3809d7a89f rust: module: use a reference in macros::module::module
When we add parameter support to the module macro, we want to be able to
pass a reference to `ModuleInfo` to a helper function. That is not possible
when we move out of the local `modinfo`. So change the function to access
the local via reference rather than value.

Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:41:29 +01:00
Andreas Hindborg
0b08fc2928 rust: introduce module_param module
Add types and traits for interfacing the C moduleparam API.

Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:40:57 +01:00
Andreas Hindborg
51d9ee90ea rust: str: add radix prefixed integer parsing functions
Add the trait `ParseInt` for parsing string representations of integers
where the string representations are optionally prefixed by a radix
specifier. Implement the trait for the primitive integer types.

Suggested-by: Benno Lossin <benno.lossin@proton.me>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:40:45 +01:00
Andreas Hindborg
821fe7bf16 rust: sync: add SetOnce
Introduce the `SetOnce` type, a container that can only be written once.
The container uses an internal atomic to synchronize writes to the internal
value.

Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Benno Lossin <lossin@kernel.org>
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
Tested-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
2025-11-03 14:40:35 +01:00
Leon Romanovsky
131971f67e dma-mapping: remove unused map_page callback
After conversion of arch code to use physical address mapping,
there are no users of .map_page() and .unmap_page() callbacks,
so let's remove them.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-14-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:31 +01:00
Leon Romanovsky
936a9f0cb1 xen: swiotlb: Convert mapping routine to rely on physical address
Switch to .map_phys callback instead of .map_page.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-13-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
33d2c5ee10 x86: Use physical address for DMA mapping
Perform mechanical conversion from DMA .map_page to .map_phys.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-12-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
38c0d0ebf5 sparc: Use physical address DMA mapping
Convert sparc architecture DMA code to use .map_phys callback.

Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-11-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
a10d648d13 powerpc: Convert to physical address DMA mapping
Adapt PowerPC DMA to use physical addresses in order to prepare code
to removal .map_page and .unmap_page.

Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-10-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
96ddf2ef58 parisc: Convert DMA map_page to map_phys interface
Perform mechanical conversion from .map_page to .map_phys callback.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-9-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
e4e3fff66a MIPS/jazzdma: Provide physical address directly
MIPS jazz uses physical addresses for mapping pages, so convert
it to get them directly from DMA mapping routine.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-8-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
6aaecdf0d8 alpha: Convert mapping routine to rely on physical address
Alpha doesn't need struct *page and can perform mapping based on
physical addresses. So convert it to implement new .map_phys callback.

As part of this change, remove useless BUG_ON() as DMA mapping layer
ensures that right direction is provided.

Tested-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-7-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
14cb413af0 dma-mapping: remove unused mapping resource callbacks
After ARM and XEN conversions to use physical addresses for the mapping,
there are no in-kernel users for map_resource/unmap_resource callbacks,
so remove them.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-6-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
af85de5a9f xen: swiotlb: Switch to physical address mapping callbacks
Combine resource and page mappings routines to one function
and remove .map_resource/.unmap_resource callbacks completely.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-5-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
50b149be07 ARM: dma-mapping: Switch to physical address mapping callbacks
Combine resource and page mappings routines to one function, which
handles both these flows at the same manner. This conversion allows
us to remove .map_resource/.unmap_resource callbacks completely.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-4-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:30 +01:00
Leon Romanovsky
52c9aa1adc ARM: dma-mapping: Reduce struct page exposure in arch_sync_dma*()
As a preparation to changing from .map_page to use .map_phys DMA
callbacks, convert arch_sync_dma*() functions to use physical addresses
instead of struct page.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-3-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:29 +01:00
Leon Romanovsky
45fa6d190d dma-mapping: convert dummy ops to physical address mapping
Change dma_dummy_map_page and dma_dummy_unmap_page routines
to accept physical address and rename them.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-2-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:29 +01:00
Leon Romanovsky
ed7fc3cbb3 dma-mapping: prepare dma_map_ops to conversion to physical address
Add new .map_phys() and .unmap_phys() callbacks to dma_map_ops as a
preparation to replace .map_page() and .unmap_page() respectively.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251015-remove-map-page-v5-1-3bbfe3a25cdf@kernel.org
2025-10-29 10:27:29 +01:00
Qinxin Xia
f74ee32963 tools/dma: move dma_map_benchmark from selftests to tools/dma
dma_map_benchmark is a standalone developer tool rather than an
automated selftest. It has no pass/fail criteria, expects manual
invocation, and is built as a normal userspace binary. Move it to
tools/dma/ and add a minimal Makefile.

Suggested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Suggested-by: Barry Song <baohua@kernel.org>
Signed-off-by: Qinxin Xia <xiaqinxin@huawei.com>
Acked-by: Barry Song <baohua@kernel.org>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/20251028120900.2265511-3-xiaqinxin@huawei.com
2025-10-29 09:41:40 +01:00
Peng Fan
12dc929c6c remoteproc: core: Remove unused export of rproc_va_to_pa
Commit 086d08725d ("remoteproc: create vdev subdevice with specific dma
memory pool") added an export for rproc_va_to_pa. However, since its
introduction, this symbol has not been used by any loadable modules. It
remains only referenced within remoteproc_virtio.c, which is always built
together with remoteproc_core.c.

As such, exporting rproc_va_to_pa is unnecessary, so remove the export.

No functional changes.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Acked-by: Andrew Davis <afd@ti.com>
Link: https://lore.kernel.org/r/20251016-rproc-cleanup-v3-v3-4-774083716e8a@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-27 09:26:07 -06:00
Peng Fan
6e863a57dd remoteproc: core: Removed unused headers
There is no user of crc32.h, debugfs.h, of_reserved_mem.h, virtio_ids.h,
so remove from the included headers.

No functional changes.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Acked-by: Andrew Davis <afd@ti.com>
Link: https://lore.kernel.org/r/20251016-rproc-cleanup-v3-v3-3-774083716e8a@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-27 09:26:01 -06:00
Peng Fan
f1b26faafd remoteproc: core: Sort header includes
Reordered the header includes in drivers/remoteproc/remoteproc_core.c
to follow alphabetical order to simplify future maintenance.

No functional changes.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Acked-by: Andrew Davis <afd@ti.com>
Link: https://lore.kernel.org/r/20251016-rproc-cleanup-v3-v3-2-774083716e8a@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-27 09:25:55 -06:00
Peng Fan
4531b6bad5 remoteproc: core: Drop redundant initialization of 'ret' in rproc_shutdown()
The variable ret is immediately assigned the return value of
mutex_lock_interruptible(), making its prior initialization to zero
unnecessary. Remove the redundant assignment

No functional changes.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Acked-by: Andrew Davis <afd@ti.com>
Link: https://lore.kernel.org/r/20251016-rproc-cleanup-v3-v3-1-774083716e8a@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-27 09:25:49 -06:00
Peng Fan
5a4d08351b remoteproc: imx_rproc: Remove the assignement to method
'method' is no longer used in imx_rproc.c, so remove the assignment.
But imx_dsp_rproc.c is still using 'method', so still keep the field
in struct imx_rrpoc_dcfg.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251024-imx_rproc_c4-v4-4-af83ed3fdbba@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-24 08:48:22 -06:00
Peng Fan
b2d66cd137 remoteproc: imx_rproc: Enable PM runtime support unconditionally
PM runtime support is safe and applicable across all i.MX platforms, not
just those using the SCU API. Remove the conditional check and enable PM
runtime unconditionally to simplify the code and ensure consistent power
management behavior.

Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251024-imx_rproc_c4-v4-3-af83ed3fdbba@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-24 08:47:26 -06:00
Peng Fan
016a3d4bcf remoteproc: imx_rproc: Make detach operation platform-specific
Refactor the detach logic to support platform-specific implementations via
the dcfg->ops->detach callback. Allow finer control over detach behavior
depending on the remote processor management method, and make it easier
to add detach support for new SoCs.

The previous hardcoded SCU API detach logic is now moved into a dedicated
imx_rproc_scu_api_detach() function, and registered via the plat ops
structure. The generic imx_rproc_detach() now delegates to the
platform-specific handler if available.

Also, the dcfg->method check with IMX_RPROC_SCU_API is removed.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251024-imx_rproc_c4-v4-2-af83ed3fdbba@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-24 08:46:33 -06:00
Peng Fan
ddbec021a3 remoteproc: imx_rproc: Simplify clock enable logic using dcfg flags
Simplify the clock enable logic by removing the dedicated
imx_rproc_clk_enable() function and integrate the clock handling directly
into the probe function to simplify the code.

Add a new IMX_RPROC_NEED_CLKS flag in dcfg to indicate whether clock
management is required for a given SoC. Update probe logic to conditionally
enable clocks based on the new flag.

Set the flag for applicable SoCs (e.g., i.MX7D, i.MX8MQ, i.MX93, etc.).

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20251024-imx_rproc_c4-v4-1-af83ed3fdbba@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-24 08:45:41 -06:00
Len Brown
6dfb04332f tools/power turbostat: Remove dead code
amperf_group_fd is never used.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:54:16 -03:00
Len Brown
696d15cbd8 tools/power turbostat: Refactor floating point printout code
Too many copies of (usually) the same printf code...

Also, unify code for added-counter FORMAT_AVERAGE,
which was correct where it was tested, but neglected elsewhere.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:54:09 -03:00
Len Brown
64f96057a6 tools/power turbostat.8: Update example
Update the added-counters example to print counters in decimal
rather than hex -- now that it is working...

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:50:32 -03:00
Len Brown
885e822764 tools/power turbostat: Refactor added-counter value printing code
We build up many copies of very similar code...

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:50:25 -03:00
Len Brown
56dbb87850 tools/power turbostat: Refactor added column header printing
Over time, we built up many copies of nearly identical code...

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:44:18 -03:00
Len Brown
4e35847d7b tools/power turbostat: Add Wildcat Lake and Nova Lake support
Treat Wildcat Lake and Nova Lake (and Panther Lake)
the same as Lunar Lake, for now.

Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:41:21 -03:00
Len Brown
92664f2e6a tools/power turbostat: Regression fix Uncore MHz printed in hex
A patch to allow specifying FORMAT_AVERAGE to added counters...
broke the internally added counter for Cluster Uncore MHz -- printing it in HEX.

Fixes: dcd1c379b0 ("tools/power turbostat: add format "average" for external attributes")
Reported-by: Andrej Tkalcec <andrej.tkalcec@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2025-10-24 10:41:17 -03:00
AngeloGioacchino Del Regno
8fd705c5e7 remoteproc: mtk_scp: Construct FW path if firmware-name not present
After a reply on the mailing lists [1] it emerged that the DT
property "firmware-name" should not be relied on because of
possible issues with firmware versions.
For MediaTek SCP, there has never been any firmware version vs
driver version desync issue but, regardless, the firmwares are
always using the same name and they're always located in a path
with a specific pattern.

Instead of unconditionally always relying on the firmware-name
devicetree property to get a path to the SCP FW file, drivers
should construct a name based on what firmware it knows and
what hardware it is running on.

In order to do that, add a `scp_get_default_fw_path()` function
that constructs the path and filename based on two of the infos
that the driver can get:
 1. The compatible string with the highest priority (so, the
    first one at index 0); and
 2. The type of SCP HW - single-core or multi-core.

This means that the default firmware path is generated as:
 - Single core SCP: mediatek/(soc_model)/scp.img
   for example:     mediatek/mt8183/scp.img;

 - Multi core SCP:  mediatek/(soc_model)/scp_c(core_number).img
   for example:     mediatek/mt8188/scp_c0.img for Core 0, and
                    mediatek/mt8188/scp_c1.img for Core 1.

Note that the generated firmware path is being used only if the
"firmware-name" devicetree property is not present in the SCP
node or in the SCP Core node(s).

[1 - Reply regarding firmware-name property]
Link: https://lore.kernel.org/all/7e8718b0-df78-44a6-a102-89529d6abcce@app.fastmail.com/
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20251015084103.10737-1-angelogioacchino.delregno@collabora.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-20 09:16:11 -06:00
Peng Fan
ff7c763b91 remoteproc: imx_rproc: Use devm_rproc_add() helper
Replace manual rproc_add() and cleanup logic with devm_rproc_add(), which
ties the remoteproc lifecycle to the device's lifecycle. This simplifies
error handling and ensures proper cleanup.

With no need to invoke rproc_del(), the remove() ops could be removed.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-6-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:14:32 -06:00
Peng Fan
9b2451658a remoteproc: imx_rproc: Use devm_add_action_or_reset() for scu cleanup
Replace the explicit call to imx_rproc_put_scu() in the remove path with
devm_add_action_or_reset(). Ensure proper cleanup of scu resources and
simplify the code by leveraging the device-managed resource framework.

Additionally:
 - Remove the IMX_RPROC_SCU_API check from imx_rproc_put_scu(), as
   devm_add_action_or_reset() now exclusively handles SCU cleanup.
 - Improve error reporting by using dev_err_probe() for consistency and
   clarity.
 - Drop the err_put_scu label, as it is now redundant due to the updated
   error handling approach.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-5-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:13:36 -06:00
Peng Fan
65af722aa8 remoteproc: imx_rproc: Use devm_clk_get_enabled() and simplify cleanup
Replace separate calls to devm_clk_get() and clk_prepare_enable() with
devm_clk_get_enabled(), which combines clock acquisition and enabling
into a single managed step. Simplify the probe logic and remove the need
for manual clock disable in error and remove paths.

Also, update error handling to eliminate redundant cleanup steps and use
return-based error propagation where appropriate. Improve code clarity and
reduce the chance of resource leaks or incorrect ordering in cleanup paths.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-4-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:12:29 -06:00
Peng Fan
b0106defc0 remoteproc: imx_rproc: Use devm_add_action_or_reset() for mailbox cleanup
Convert imx_rproc_free_mbox() to a devm-managed cleanup action using
devm_add_action_or_reset(). Ensure the mailbox resources are freed
automatically with the device lifecycle, simplify error handling and
removing the need for manual cleanup in probe and remove paths.

Also improve error reporting by using dev_err_probe() for consistency and
clarity.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-3-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:11:39 -06:00
Peng Fan
6c5c37dc41 remoteproc: imx_rproc: Use devm_add_action_or_reset() for workqueue cleanup
Replace manual destroy_workqueue() calls in error and remove paths with a
devm_add_action_or_reset() helper. Ensure the workqueue is properly
cleaned up with the device lifecycle, and simplify error handling in probe
by removing now-unnecessary labels and cleanup steps.

No functional changes.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-2-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:09:40 -06:00
Peng Fan
80405a34e1 remoteproc: imx_rproc: Fix runtime PM cleanup and improve remove path
Proper cleanup should be done when rproc_add() fails by invoking both
pm_runtime_disable() and pm_runtime_put_noidle() to avoid leaving the
device in an inconsistent power state.

Fix it by adding pm_runtime_put_noidle() and pm_runtime_disable()
in the error path.

Also Update the remove() callback to use pm_runtime_put_noidle() instead of
pm_runtime_put(), to clearly indicate that only need to restore the usage
count.

Fixes: a876a3aacc ("remoteproc: imx_rproc: detect and attach to pre-booted remote cores")
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Hiago De Franco <hiago.franco@toradex.com>
Suggested-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Link: https://lore.kernel.org/r/20250926-imx_rproc_v3-v3-1-4c0ec279cc5f@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2025-10-14 09:08:32 -06:00
227 changed files with 11423 additions and 2675 deletions

View File

@@ -621,3 +621,84 @@ Description:
number extended capability. The file is read only and due to
the possible sensitivity of accessible serial numbers, admin
only.
What: /sys/bus/pci/devices/.../tsm/
Contact: linux-coco@lists.linux.dev
Description:
This directory only appears if a physical device function
supports authentication (PCIe CMA-SPDM), interface security
(PCIe TDISP), and is accepted for secure operation by the
platform TSM driver. This attribute directory appears
dynamically after the platform TSM driver loads. So, only after
the /sys/class/tsm/tsm0 device arrives can tools assume that
devices without a tsm/ attribute directory will never have one;
before that, the security capabilities of the device relative to
the platform TSM are unknown. See
Documentation/ABI/testing/sysfs-class-tsm.
What: /sys/bus/pci/devices/.../tsm/connect
Contact: linux-coco@lists.linux.dev
Description:
(RW) Write the name of a TSM (TEE Security Manager) device from
/sys/class/tsm to this file to establish a connection with the
device. This typically includes an SPDM (DMTF Security
Protocols and Data Models) session over PCIe DOE (Data Object
Exchange) and may also include PCIe IDE (Integrity and Data
Encryption) establishment. Reads from this attribute return the
name of the connected TSM or the empty string if not
connected. A TSM device signals its readiness to accept PCI
connection via a KOBJ_CHANGE event.
What: /sys/bus/pci/devices/.../tsm/disconnect
Contact: linux-coco@lists.linux.dev
Description:
(WO) Write the name of the TSM device that was specified
to 'connect' to teardown the connection.
What: /sys/bus/pci/devices/.../tsm/dsm
Contact: linux-coco@lists.linux.dev
Description: (RO) Return PCI device name of this device's DSM (Device
Security Manager). When a device is in the connected state it
indicates that the platform TSM (TEE Security Manager) has made
a secure-session connection with a device's DSM. A DSM is always
physical function 0 and when the device supports TDISP (TEE
Device Interface Security Protocol) its managed functions also
populate this tsm/dsm attribute. The managed functions of a DSM
are SR-IOV (Single Root I/O Virtualization) virtual functions,
non-zero functions of a multi-function device, or downstream
endpoints depending on whether the DSM is an SR-IOV physical
function, function0 of a multi-function device, or an upstream
PCIe switch port. This is a "link" TSM attribute, see
Documentation/ABI/testing/sysfs-class-tsm.
What: /sys/bus/pci/devices/.../tsm/bound
Contact: linux-coco@lists.linux.dev
Description: (RO) Return the device name of the TSM when the device is in a
TDISP (TEE Device Interface Security Protocol) operational state
(LOCKED, RUN, or ERROR, not UNLOCKED). Bound devices consume
platform TSM resources and depend on the device's configuration
(e.g. BME (Bus Master Enable) and MSE (Memory Space Enable)
among other settings) to remain stable for the duration of the
bound state. This attribute is only visible for devices that
support TDISP operation, and it is only populated after
successful connect and TSM bind. The TSM bind operation is
initiated by VFIO/IOMMUFD. This is a "link" TSM attribute, see
Documentation/ABI/testing/sysfs-class-tsm.
What: /sys/bus/pci/devices/.../authenticated
Contact: linux-pci@vger.kernel.org
Description:
When the device's tsm/ directory is present device
authentication (PCIe CMA-SPDM) and link encryption (PCIe IDE)
are handled by the platform TSM (TEE Security Manager). When the
tsm/ directory is not present this attribute reflects only the
native CMA-SPDM authentication state with the kernel's
certificate store.
If the attribute is not present, it indicates that
authentication is unsupported by the device, or the TSM has no
available authentication methods for the device.
When present and the tsm/ attribute directory is present, the
authenticated attribute is an alias for the device 'connect'
state. See the 'tsm/connect' attribute for more details.

View File

@@ -0,0 +1,19 @@
What: /sys/class/tsm/tsmN
Contact: linux-coco@lists.linux.dev
Description:
"tsmN" is a device that represents the generic attributes of a
platform TEE Security Manager. It is typically a child of a
platform enumerated TSM device. /sys/class/tsm/tsmN/uevent
signals when the PCI layer is able to support establishment of
link encryption and other device-security features coordinated
through a platform tsm.
What: /sys/class/tsm/tsmN/streamH.R.E
Contact: linux-pci@vger.kernel.org
Description:
(RO) When a host bridge has established a secure connection via
the platform TSM, symlink appears. The primary function of this
is have a system global review of TSM resource consumption
across host bridges. The link points to the endpoint PCI device
and matches the same link published by the host bridge. See
Documentation/ABI/testing/sysfs-devices-pci-host-bridge.

View File

@@ -0,0 +1,45 @@
What: /sys/devices/pciDDDD:BB
/sys/devices/.../pciDDDD:BB
Contact: linux-pci@vger.kernel.org
Description:
A PCI host bridge device parents a PCI bus device topology. PCI
controllers may also parent host bridges. The DDDD:BB format
conveys the PCI domain (ACPI segment) number and root bus number
(in hexadecimal) of the host bridge. Note that the domain number
may be larger than the 16-bits that the "DDDD" format implies
for emulated host-bridges.
What: pciDDDD:BB/firmware_node
Contact: linux-pci@vger.kernel.org
Description:
(RO) Symlink to the platform firmware device object "companion"
of the host bridge. For example, an ACPI device with an _HID of
PNP0A08 (/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00). See
/sys/devices/pciDDDD:BB entry for details about the DDDD:BB
format.
What: pciDDDD:BB/streamH.R.E
Contact: linux-pci@vger.kernel.org
Description:
(RO) When a platform has established a secure connection, PCIe
IDE, between two Partner Ports, this symlink appears. A stream
consumes a Stream ID slot in each of the Host bridge (H), Root
Port (R) and Endpoint (E). The link points to the Endpoint PCI
device in the Selective IDE Stream pairing. Specifically, "R"
and "E" represent the assigned Selective IDE Stream Register
Block in the Root Port and Endpoint, and "H" represents a
platform specific pool of stream resources shared by the Root
Ports in a host bridge. See /sys/devices/pciDDDD:BB entry for
details about the DDDD:BB format.
What: pciDDDD:BB/available_secure_streams
Contact: linux-pci@vger.kernel.org
Description:
(RO) When a host bridge has Root Ports that support PCIe IDE
(link encryption and integrity protection) there may be a
limited number of Selective IDE Streams that can be used for
establishing new end-to-end secure links. This attribute
decrements upon secure link setup, and increments upon secure
link teardown. The in-use stream count is determined by counting
stream symlinks. See /sys/devices/pciDDDD:BB entry for details
about the DDDD:BB format.

View File

@@ -57,8 +57,7 @@ properties:
- prstb
- intb-only
timeout-sec:
maxItems: 2
timeout-sec: true
regulators:
$ref: /schemas/regulator/rohm,bd96801-regulator.yaml
@@ -72,7 +71,10 @@ required:
- interrupt-names
- regulators
additionalProperties: false
allOf:
- $ref: /schemas/watchdog/watchdog.yaml
unevaluatedProperties: false
examples:
- |

View File

@@ -24,6 +24,7 @@ properties:
- qcom,msm8998-adsp-pas
- qcom,msm8998-slpi-pas
- qcom,sdm660-adsp-pas
- qcom,sdm660-cdsp-pas
- qcom,sdm845-adsp-pas
- qcom,sdm845-cdsp-pas
- qcom,sdm845-slpi-pas
@@ -31,9 +32,6 @@ properties:
reg:
maxItems: 1
cx-supply:
description: Phandle to the CX regulator
px-supply:
description: Phandle to the PX regulator
@@ -69,6 +67,8 @@ allOf:
- qcom,msm8996-slpi-pil
- qcom,msm8998-adsp-pas
- qcom,msm8998-slpi-pas
- qcom,sdm660-adsp-pas
- qcom,sdm660-cdsp-pas
- qcom,sdm845-adsp-pas
- qcom,sdm845-cdsp-pas
- qcom,sdm845-slpi-pas
@@ -93,6 +93,8 @@ allOf:
- qcom,msm8996-slpi-pil
- qcom,msm8998-adsp-pas
- qcom,msm8998-slpi-pas
- qcom,sdm660-adsp-pas
- qcom,sdm660-cdsp-pas
- qcom,sdm845-adsp-pas
- qcom,sdm845-cdsp-pas
- qcom,sdm845-slpi-pas
@@ -103,16 +105,6 @@ allOf:
interrupt-names:
maxItems: 5
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8974-adsp-pil
then:
required:
- cx-supply
- if:
properties:
compatible:
@@ -120,8 +112,11 @@ allOf:
enum:
- qcom,msm8226-adsp-pil
- qcom,msm8953-adsp-pil
- qcom,msm8974-adsp-pil
- qcom,msm8996-adsp-pil
- qcom,msm8998-adsp-pas
- qcom,sdm660-adsp-pas
- qcom,sdm660-cdsp-pas
then:
properties:
power-domains:
@@ -178,6 +173,7 @@ allOf:
- qcom,msm8998-adsp-pas
- qcom,msm8998-slpi-pas
- qcom,sdm660-adsp-pas
- qcom,sdm660-cdsp-pas
then:
properties:
qcom,qmp: false
@@ -187,6 +183,7 @@ examples:
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/power/qcom-rpmpd.h>
adsp {
compatible = "qcom,msm8974-adsp-pil";
@@ -204,7 +201,8 @@ examples:
clocks = <&rpmcc RPM_CXO_CLK>;
clock-names = "xo";
cx-supply = <&pm8841_s2>;
power-domains = <&rpmpd MSM8974_VDDCX>;
power-domain-names = "cx";
memory-region = <&adsp_region>;

View File

@@ -91,9 +91,13 @@ allOf:
power-domains:
items:
- description: NSP power domain
- description: CX power domain
- description: MXC power domain
power-domain-names:
items:
- const: nsp
- const: cx
- const: mxc
unevaluatedProperties: false

View File

@@ -14,7 +14,11 @@ allOf:
properties:
compatible:
const: airoha,en7581-wdt
oneOf:
- items:
- const: airoha,an7583-wdt
- const: airoha,en7581-wdt
- const: airoha,en7581-wdt
reg:
maxItems: 1

View File

@@ -15,6 +15,7 @@ properties:
- aspeed,ast2400-wdt
- aspeed,ast2500-wdt
- aspeed,ast2600-wdt
- aspeed,ast2700-wdt
reg:
maxItems: 1
@@ -87,13 +88,15 @@ properties:
aspeed,reset-mask:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 2
maxItems: 5
description: >
A bitmask indicating which peripherals will be reset if the watchdog
timer expires. On AST2500 SoCs this should be a single word defined using
the AST2500_WDT_RESET_* macros; on AST2600 SoCs this should be a two-word
array with the first word defined using the AST2600_WDT_RESET1_* macros,
and the second word defined using the AST2600_WDT_RESET2_* macros.
and the second word defined using the AST2600_WDT_RESET2_* macros; on
AST2700 SoCs, this should be five-word array from AST2700_WDT_RESET1_*
macros to AST2700_WDT_RESET5_* macros.
required:
- compatible
@@ -114,6 +117,7 @@ allOf:
enum:
- aspeed,ast2500-wdt
- aspeed,ast2600-wdt
- aspeed,ast2700-wdt
- if:
required:
- aspeed,ext-active-high

View File

@@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/lantiq,wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Lantiq WTD watchdog
maintainers:
- Hauke Mehrtens <hauke@hauke-m.de>
properties:
compatible:
oneOf:
- enum:
- lantiq,falcon-wdt
- lantiq,wdt
- lantiq,xrx100-wdt
- items:
- enum:
- lantiq,xrx200-wdt
- const: lantiq,xrx100-wdt
reg:
maxItems: 1
lantiq,rcu:
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle to the RCU syscon node
required:
- compatible
- reg
allOf:
- $ref: watchdog.yaml#
- if:
properties:
compatible:
contains:
enum:
- lantiq,xrx100-wdt
- lantiq,falcon-wdt
then:
required:
- lantiq,rcu
unevaluatedProperties: false
examples:
- |
watchdog@803f0 {
compatible = "lantiq,xrx200-wdt", "lantiq,xrx100-wdt";
reg = <0x803f0 0x10>;
lantiq,rcu = <&rcu0>;
};

View File

@@ -1,24 +0,0 @@
Lantiq WTD watchdog binding
============================
This describes the binding of the Lantiq watchdog driver.
-------------------------------------------------------------------------------
Required properties:
- compatible : Should be one of
"lantiq,wdt"
"lantiq,xrx100-wdt"
"lantiq,xrx200-wdt", "lantiq,xrx100-wdt"
"lantiq,falcon-wdt"
- reg : Address of the watchdog block
- lantiq,rcu : A phandle to the RCU syscon (required for
"lantiq,falcon-wdt" and "lantiq,xrx100-wdt")
-------------------------------------------------------------------------------
Example for the watchdog on the xRX200 SoCs:
watchdog@803f0 {
compatible = "lantiq,xrx200-wdt", "lantiq,xrx100-wdt";
reg = <0x803f0 0x10>;
lantiq,rcu = <&rcu0>;
};

View File

@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/watchdog/loongson,ls1x-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Loongson-1 Watchdog Timer
title: Loongson Watchdog Timer
maintainers:
- Keguang Zhang <keguang.zhang@gmail.com>
@@ -17,6 +17,7 @@ properties:
enum:
- loongson,ls1b-wdt
- loongson,ls1c-wdt
- loongson,ls2k0300-wdt
reg:
maxItems: 1

View File

@@ -1,45 +0,0 @@
* Marvell Orion Watchdog Time
Required Properties:
- Compatibility : "marvell,orion-wdt"
"marvell,armada-370-wdt"
"marvell,armada-xp-wdt"
"marvell,armada-375-wdt"
"marvell,armada-380-wdt"
- reg : Should contain two entries: first one with the
timer control address, second one with the
rstout enable address.
For "marvell,armada-375-wdt" and "marvell,armada-380-wdt":
- reg : A third entry is mandatory and should contain the
shared mask/unmask RSTOUT address.
Clocks required for compatibles = "marvell,orion-wdt",
"marvell,armada-370-wdt":
- clocks : Must contain a single entry describing the clock input
Clocks required for compatibles = "marvell,armada-xp-wdt"
"marvell,armada-375-wdt"
"marvell,armada-380-wdt":
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : Must include the following entries:
"nbclk" (L2/coherency fabric clock),
"fixed" (Reference 25 MHz fixed-clock).
Optional properties:
- interrupts : Contains the IRQ for watchdog expiration
- timeout-sec : Contains the watchdog timeout in seconds
Example:
wdt@20300 {
compatible = "marvell,orion-wdt";
reg = <0x20300 0x28>, <0x20108 0x4>;
interrupts = <3>;
timeout-sec = <10>;
clocks = <&gate_clk 7>;
};

View File

@@ -0,0 +1,100 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/marvell,orion-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Marvell Orion Watchdog Timer
maintainers:
- Andrew Lunn <andrew@lunn.ch>
- Gregory Clement <gregory.clement@bootlin.com>
properties:
compatible:
enum:
- marvell,orion-wdt
- marvell,armada-370-wdt
- marvell,armada-xp-wdt
- marvell,armada-375-wdt
- marvell,armada-380-wdt
reg:
minItems: 2
items:
- description: Timer control register address
- description: RSTOUT enable register address
- description: Shared mask/unmask RSTOUT register address
clocks:
minItems: 1
items:
- description: L2/coherency fabric clock input
- description: Reference 25 MHz fixed-clock supply
clock-names:
minItems: 1
items:
- const: nbclk
- const: fixed
interrupts:
minItems: 1
items:
- description: timeout
- description: pre-timeout
allOf:
- $ref: watchdog.yaml#
- if:
properties:
compatible:
contains:
enum:
- marvell,armada-375-wdt
- marvell,armada-380-wdt
then:
properties:
reg:
minItems: 3
else:
properties:
reg:
maxItems: 2
- if:
properties:
compatible:
contains:
enum:
- marvell,armada-xp-wdt
- marvell,armada-375-wdt
- marvell,armada-380-wdt
then:
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
interrupts:
minItems: 2
required:
- clock-names
required:
- compatible
- reg
- clocks
unevaluatedProperties: false
examples:
- |
watchdog@20300 {
compatible = "marvell,orion-wdt";
reg = <0x20300 0x28>, <0x20108 0x4>;
interrupts = <3>;
timeout-sec = <10>;
clocks = <&gate_clk 7>;
};

View File

@@ -41,6 +41,8 @@ properties:
- mediatek,mt7623-wdt
- mediatek,mt7629-wdt
- mediatek,mt8173-wdt
- mediatek,mt8188-wdt
- mediatek,mt8189-wdt
- mediatek,mt8365-wdt
- mediatek,mt8516-wdt
- const: mediatek,mt6589-wdt

View File

@@ -1,15 +0,0 @@
TI Watchdog Timer (WDT) Controller for OMAP
Required properties:
- compatible : "ti,omap3-wdt" for OMAP3 or "ti,omap4-wdt" for OMAP4
- ti,hwmods : Name of the hwmod associated to the WDT
Optional properties:
- timeout-sec : default watchdog timeout in seconds
Examples:
wdt2: wdt@4a314000 {
compatible = "ti,omap4-wdt", "ti,omap3-wdt";
ti,hwmods = "wd_timer2";
};

View File

@@ -22,6 +22,7 @@ properties:
- qcom,apss-wdt-ipq5332
- qcom,apss-wdt-ipq5424
- qcom,apss-wdt-ipq9574
- qcom,apss-wdt-kaanapali
- qcom,apss-wdt-msm8226
- qcom,apss-wdt-msm8974
- qcom,apss-wdt-msm8994

View File

@@ -0,0 +1,99 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/renesas,r9a09g057-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/V2H(P) Watchdog Timer (WDT) Controller
maintainers:
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
properties:
compatible:
oneOf:
- items:
- enum:
- renesas,r9a09g047-wdt # RZ/G3E
- renesas,r9a09g056-wdt # RZ/V2N
- const: renesas,r9a09g057-wdt # RZ/V2H(P)
- items:
- const: renesas,r9a09g087-wdt # RZ/N2H
- const: renesas,r9a09g077-wdt # RZ/T2H
- enum:
- renesas,r9a09g057-wdt # RZ/V2H(P)
- renesas,r9a09g077-wdt # RZ/T2H
reg:
minItems: 1
maxItems: 2
clocks:
minItems: 1
items:
- description: Register access clock
- description: Main clock
clock-names:
minItems: 1
items:
- const: pclk
- const: oscclk
power-domains:
maxItems: 1
resets:
maxItems: 1
timeout-sec: true
required:
- compatible
- reg
- clocks
- clock-names
- power-domains
allOf:
- $ref: watchdog.yaml#
- if:
properties:
compatible:
contains:
const: renesas,r9a09g057-wdt
then:
properties:
reg:
maxItems: 1
clocks:
minItems: 2
clock-names:
minItems: 2
else:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
reg:
minItems: 2
resets: false
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/renesas,r9a09g057-cpg.h>
watchdog@11c00400 {
compatible = "renesas,r9a09g057-wdt";
reg = <0x11c00400 0x400>;
clocks = <&cpg CPG_MOD 0x4b>, <&cpg CPG_MOD 0x4c>;
clock-names = "pclk", "oscclk";
resets = <&cpg 0x75>;
power-domains = <&cpg>;
};

View File

@@ -0,0 +1,114 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/renesas,rcar-gen3-wwdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas Window Watchdog Timer (WWDT) Controller
maintainers:
- Wolfram Sang <wsa+renesas@sang-engineering.com>
properties:
compatible:
oneOf:
- items:
- enum:
- renesas,r8a77970-wwdt # R-Car V3M
- renesas,r8a77980-wwdt # R-Car V3H
- const: renesas,rcar-gen3-wwdt
- items:
- enum:
- renesas,r8a779a0-wwdt # R-Car V3U
- renesas,r8a779f0-wwdt # R-Car S4
- renesas,r8a779g0-wwdt # R-Car V4H
- renesas,r8a779h0-wwdt # R-Car V4M
- const: renesas,rcar-gen4-wwdt
reg:
maxItems: 1
interrupts:
items:
- description: Pretimeout, 75% of overflow reached
- description: Error occurred
interrupt-names:
items:
- const: pretimeout
- const: error
clocks:
items:
- description: Counting clock
- description: Bus clock
clock-names:
items:
- const: cnt
- const: bus
resets:
minItems: 1
maxItems: 2
reset-names:
minItems: 1
items:
- const: cnt
- const: bus
power-domains:
maxItems: 1
required:
- compatible
- reg
- interrupts
- interrupt-names
- clocks
- clock-names
- resets
- reset-names
- power-domains
allOf:
- $ref: watchdog.yaml#
- if:
properties:
compatible:
contains:
enum:
- renesas,r8a779a0-wwdt
- renesas,r8a779f0-wwdt
then:
properties:
resets:
minItems: 2
reset-names:
minItems: 2
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
#include <dt-bindings/power/r8a779g0-sysc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
watchdog@ffc90000 {
compatible = "renesas,r8a779g0-wwdt",
"renesas,rcar-gen4-wwdt";
reg = <0xffc90000 0x10>;
interrupts = <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "pretimeout", "error";
clocks = <&cpg CPG_CORE R8A779G0_CLK_R>,
<&cpg CPG_CORE R8A779G0_CLK_SASYNCRT>;
clock-names = "cnt", "bus";
power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
resets = <&cpg 1200>;
reset-names = "cnt";
};

View File

@@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/renesas,rza-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/A Watchdog Timer (WDT) Controller
maintainers:
- Wolfram Sang <wsa+renesas@sang-engineering.com>
properties:
compatible:
items:
- enum:
- renesas,r7s72100-wdt # RZ/A1
- renesas,r7s9210-wdt # RZ/A2
- const: renesas,rza-wdt # RZ/A
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
timeout-sec: true
required:
- compatible
- reg
- clocks
allOf:
- $ref: watchdog.yaml#
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r7s72100-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
watchdog@fcfe0000 {
compatible = "renesas,r7s72100-wdt", "renesas,rza-wdt";
reg = <0xfcfe0000 0x6>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&p0_clk>;
};

View File

@@ -0,0 +1,111 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/renesas,rzg2l-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G2L Watchdog Timer (WDT) Controller
maintainers:
- Biju Das <biju.das.jz@bp.renesas.com>
properties:
compatible:
oneOf:
- items:
- enum:
- renesas,r9a07g043-wdt # RZ/G2UL and RZ/Five
- renesas,r9a07g044-wdt # RZ/G2{L,LC}
- renesas,r9a07g054-wdt # RZ/V2L
- renesas,r9a08g045-wdt # RZ/G3S
- const: renesas,rzg2l-wdt
- items:
- const: renesas,r9a09g011-wdt # RZ/V2M
- const: renesas,rzv2m-wdt # RZ/V2M
reg:
maxItems: 1
interrupts:
minItems: 1
items:
- description: Timeout
- description: Parity error
interrupt-names:
minItems: 1
items:
- const: wdt
- const: perrout
clocks:
items:
- description: Register access clock
- description: Main clock
clock-names:
items:
- const: pclk
- const: oscclk
power-domains:
maxItems: 1
resets:
maxItems: 1
timeout-sec: true
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- power-domains
- resets
allOf:
- $ref: watchdog.yaml#
- if:
properties:
compatible:
contains:
const: renesas,rzg2l-wdt
then:
properties:
interrupts:
minItems: 2
interrupt-names:
minItems: 2
required:
- interrupt-names
else:
properties:
interrupts:
maxItems: 1
interrupt-names:
maxItems: 1
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r9a07g044-cpg.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
watchdog@12800800 {
compatible = "renesas,r9a07g044-wdt",
"renesas,rzg2l-wdt";
reg = <0x12800800 0x400>;
clocks = <&cpg CPG_MOD R9A07G044_WDT0_PCLK>,
<&cpg CPG_MOD R9A07G044_WDT0_CLK>;
clock-names = "pclk", "oscclk";
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "wdt", "perrout";
resets = <&cpg R9A07G044_WDT0_PRESETN>;
power-domains = <&cpg>;
};

View File

@@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/renesas,rzn1-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/N1 Watchdog Timer (WDT) Controller
maintainers:
- Wolfram Sang <wsa+renesas@sang-engineering.com>
properties:
compatible:
items:
- const: renesas,r9a06g032-wdt # RZ/N1D
- const: renesas,rzn1-wdt # RZ/N1
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
timeout-sec: true
required:
- compatible
- reg
- interrupts
- clocks
allOf:
- $ref: watchdog.yaml#
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
watchdog@40008000 {
compatible = "renesas,r9a06g032-wdt", "renesas,rzn1-wdt";
reg = <0x40008000 0x1000>;
interrupts = <GIC_SPI 73 IRQ_TYPE_EDGE_RISING>;
clocks = <&sysctrl R9A06G032_CLK_WATCHDOG>;
};

View File

@@ -13,30 +13,6 @@ maintainers:
properties:
compatible:
oneOf:
- items:
- enum:
- renesas,r7s72100-wdt # RZ/A1
- renesas,r7s9210-wdt # RZ/A2
- const: renesas,rza-wdt # RZ/A
- items:
- enum:
- renesas,r9a06g032-wdt # RZ/N1D
- const: renesas,rzn1-wdt # RZ/N1
- items:
- enum:
- renesas,r9a07g043-wdt # RZ/G2UL and RZ/Five
- renesas,r9a07g044-wdt # RZ/G2{L,LC}
- renesas,r9a07g054-wdt # RZ/V2L
- renesas,r9a08g045-wdt # RZ/G3S
- const: renesas,rzg2l-wdt
- items:
- enum:
- renesas,r9a09g011-wdt # RZ/V2M
- const: renesas,rzv2m-wdt # RZ/V2M
- items:
- enum:
- renesas,r8a7742-wdt # RZ/G1H
@@ -75,47 +51,14 @@ properties:
- renesas,r8a779h0-wdt # R-Car V4M
- const: renesas,rcar-gen4-wdt # R-Car Gen4
- items:
- enum:
- renesas,r9a09g047-wdt # RZ/G3E
- renesas,r9a09g056-wdt # RZ/V2N
- const: renesas,r9a09g057-wdt # RZ/V2H(P)
- enum:
- renesas,r9a09g057-wdt # RZ/V2H(P)
- renesas,r9a09g077-wdt # RZ/T2H
- items:
- const: renesas,r9a09g087-wdt # RZ/N2H
- const: renesas,r9a09g077-wdt # RZ/T2H
reg:
minItems: 1
maxItems: 2
maxItems: 1
interrupts:
minItems: 1
items:
- description: Timeout
- description: Parity error
interrupt-names:
minItems: 1
items:
- const: wdt
- const: perrout
maxItems: 1
clocks:
minItems: 1
items:
- description: Register access clock
- description: Main clock
clock-names:
minItems: 1
items:
- const: pclk
- const: oscclk
maxItems: 1
power-domains:
maxItems: 1
@@ -129,6 +72,8 @@ required:
- compatible
- reg
- clocks
- interrupts
- power-domains
allOf:
- $ref: watchdog.yaml#
@@ -138,90 +83,11 @@ allOf:
properties:
compatible:
contains:
enum:
- renesas,r9a09g077-wdt
- renesas,rza-wdt
- renesas,rzn1-wdt
const: renesas,r8a77980-wdt
then:
required:
- power-domains
- resets
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g057-wdt
- renesas,rzg2l-wdt
- renesas,rzv2m-wdt
then:
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
required:
- clock-names
else:
properties:
clocks:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- renesas,rzg2l-wdt
then:
properties:
interrupts:
minItems: 2
interrupt-names:
minItems: 2
required:
- interrupt-names
else:
properties:
interrupts:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g057-wdt
- renesas,r9a09g077-wdt
then:
properties:
interrupts: false
interrupt-names: false
else:
required:
- interrupts
- if:
properties:
compatible:
contains:
const: renesas,r9a09g077-wdt
then:
properties:
resets: false
clock-names:
maxItems: 1
reg:
minItems: 2
required:
- clock-names
- power-domains
else:
properties:
reg:
maxItems: 1
additionalProperties: false
examples:

View File

@@ -28,6 +28,7 @@ properties:
- rockchip,rk3328-wdt
- rockchip,rk3368-wdt
- rockchip,rk3399-wdt
- rockchip,rk3506-wdt
- rockchip,rk3562-wdt
- rockchip,rk3568-wdt
- rockchip,rk3576-wdt

View File

@@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/ti,omap2-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI OMAP Watchdog Timer Controller
maintainers:
- Aaro Koskinen <aaro.koskinen@iki.fi>
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
oneOf:
- enum:
- ti,omap2-wdt
- ti,omap3-wdt
- items:
- enum:
- ti,am4372-wdt
- ti,omap4-wdt
- ti,omap5-wdt
- const: ti,omap3-wdt
reg:
maxItems: 1
interrupts:
maxItems: 1
ti,hwmods:
description: Name of the hardware module associated with the watchdog.
$ref: /schemas/types.yaml#/definitions/string
deprecated: true
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
watchdog@48314000 {
compatible = "ti,omap3-wdt";
reg = <0x48314000 0x80>;
ti,hwmods = "wd_timer2";
};

View File

@@ -21,9 +21,10 @@ select:
properties:
$nodename:
pattern: "^(timer|watchdog)(@.*|-([0-9]|[1-9][0-9]+))?$"
pattern: "^(pmic|timer|watchdog)(@.*|-([0-9]|[1-9][0-9]+))?$"
timeout-sec:
maxItems: 1
description:
Contains the watchdog timeout in seconds.

View File

@@ -83,7 +83,7 @@ flags, and the remaining form the internal block number.
======== =============================================================
Bit Description
======== =============================================================
31 - 30 Error and Zero flags - Used in the following way::
31 - 30 Error and Zero flags - Used in the following way:
== == ====================================================
31 30 Description

View File

@@ -10,6 +10,7 @@ The Linux PCI driver implementer's API guide
pci
p2pdma
tsm
.. only:: subproject and html

View File

@@ -0,0 +1,21 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
========================================================
PCI Trusted Execution Environment Security Manager (TSM)
========================================================
Subsystem Interfaces
====================
.. kernel-doc:: include/linux/pci-ide.h
:internal:
.. kernel-doc:: drivers/pci/ide.c
:export:
.. kernel-doc:: include/linux/pci-tsm.h
:internal:
.. kernel-doc:: drivers/pci/tsm.c
:export:

View File

@@ -13,5 +13,6 @@ NFS
rpc-cache
rpc-server-gss
nfs41-server
nfsd-io-modes
knfsd-stats
reexport

View File

@@ -0,0 +1,153 @@
.. SPDX-License-Identifier: GPL-2.0
=============
NFSD IO MODES
=============
Overview
========
NFSD has historically always used buffered IO when servicing READ and
WRITE operations. BUFFERED is NFSD's default IO mode, but it is possible
to override that default to use either DONTCACHE or DIRECT IO modes.
Experimental NFSD debugfs interfaces are available to allow the NFSD IO
mode used for READ and WRITE to be configured independently. See both:
- /sys/kernel/debug/nfsd/io_cache_read
- /sys/kernel/debug/nfsd/io_cache_write
The default value for both io_cache_read and io_cache_write reflects
NFSD's default IO mode (which is NFSD_IO_BUFFERED=0).
Based on the configured settings, NFSD's IO will either be:
- cached using page cache (NFSD_IO_BUFFERED=0)
- cached but removed from page cache on completion (NFSD_IO_DONTCACHE=1)
- not cached stable_how=NFS_UNSTABLE (NFSD_IO_DIRECT=2)
To set an NFSD IO mode, write a supported value (0 - 2) to the
corresponding IO operation's debugfs interface, e.g.::
echo 2 > /sys/kernel/debug/nfsd/io_cache_read
echo 2 > /sys/kernel/debug/nfsd/io_cache_write
To check which IO mode NFSD is using for READ or WRITE, simply read the
corresponding IO operation's debugfs interface, e.g.::
cat /sys/kernel/debug/nfsd/io_cache_read
cat /sys/kernel/debug/nfsd/io_cache_write
If you experiment with NFSD's IO modes on a recent kernel and have
interesting results, please report them to linux-nfs@vger.kernel.org
NFSD DONTCACHE
==============
DONTCACHE offers a hybrid approach to servicing IO that aims to offer
the benefits of using DIRECT IO without any of the strict alignment
requirements that DIRECT IO imposes. To achieve this buffered IO is used
but the IO is flagged to "drop behind" (meaning associated pages are
dropped from the page cache) when IO completes.
DONTCACHE aims to avoid what has proven to be a fairly significant
limition of Linux's memory management subsystem if/when large amounts of
data is infrequently accessed (e.g. read once _or_ written once but not
read until much later). Such use-cases are particularly problematic
because the page cache will eventually become a bottleneck to servicing
new IO requests.
For more context on DONTCACHE, please see these Linux commit headers:
- Overview: 9ad6344568cc3 ("mm/filemap: change filemap_create_folio()
to take a struct kiocb")
- for READ: 8026e49bff9b1 ("mm/filemap: add read support for
RWF_DONTCACHE")
- for WRITE: 974c5e6139db3 ("xfs: flag as supporting FOP_DONTCACHE")
NFSD_IO_DONTCACHE will fall back to NFSD_IO_BUFFERED if the underlying
filesystem doesn't indicate support by setting FOP_DONTCACHE.
NFSD DIRECT
===========
DIRECT IO doesn't make use of the page cache, as such it is able to
avoid the Linux memory management's page reclaim scalability problems
without resorting to the hybrid use of page cache that DONTCACHE does.
Some workloads benefit from NFSD avoiding the page cache, particularly
those with a working set that is significantly larger than available
system memory. The pathological worst-case workload that NFSD DIRECT has
proven to help most is: NFS client issuing large sequential IO to a file
that is 2-3 times larger than the NFS server's available system memory.
The reason for such improvement is NFSD DIRECT eliminates a lot of work
that the memory management subsystem would otherwise be required to
perform (e.g. page allocation, dirty writeback, page reclaim). When
using NFSD DIRECT, kswapd and kcompactd are no longer commanding CPU
time trying to find adequate free pages so that forward IO progress can
be made.
The performance win associated with using NFSD DIRECT was previously
discussed on linux-nfs, see:
https://lore.kernel.org/linux-nfs/aEslwqa9iMeZjjlV@kernel.org/
But in summary:
- NFSD DIRECT can significantly reduce memory requirements
- NFSD DIRECT can reduce CPU load by avoiding costly page reclaim work
- NFSD DIRECT can offer more deterministic IO performance
As always, your mileage may vary and so it is important to carefully
consider if/when it is beneficial to make use of NFSD DIRECT. When
assessing comparative performance of your workload please be sure to log
relevant performance metrics during testing (e.g. memory usage, cpu
usage, IO performance). Using perf to collect perf data that may be used
to generate a "flamegraph" for work Linux must perform on behalf of your
test is a really meaningful way to compare the relative health of the
system and how switching NFSD's IO mode changes what is observed.
If NFSD_IO_DIRECT is specified by writing 2 (or 3 and 4 for WRITE) to
NFSD's debugfs interfaces, ideally the IO will be aligned relative to
the underlying block device's logical_block_size. Also the memory buffer
used to store the READ or WRITE payload must be aligned relative to the
underlying block device's dma_alignment.
But NFSD DIRECT does handle misaligned IO in terms of O_DIRECT as best
it can:
Misaligned READ:
If NFSD_IO_DIRECT is used, expand any misaligned READ to the next
DIO-aligned block (on either end of the READ). The expanded READ is
verified to have proper offset/len (logical_block_size) and
dma_alignment checking.
Misaligned WRITE:
If NFSD_IO_DIRECT is used, split any misaligned WRITE into a start,
middle and end as needed. The large middle segment is DIO-aligned
and the start and/or end are misaligned. Buffered IO is used for the
misaligned segments and O_DIRECT is used for the middle DIO-aligned
segment. DONTCACHE buffered IO is _not_ used for the misaligned
segments because using normal buffered IO offers significant RMW
performance benefit when handling streaming misaligned WRITEs.
Tracing:
The nfsd_read_direct trace event shows how NFSD expands any
misaligned READ to the next DIO-aligned block (on either end of the
original READ, as needed).
This combination of trace events is useful for READs::
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_read_vector/enable
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_read_direct/enable
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_read_io_done/enable
echo 1 > /sys/kernel/tracing/events/xfs/xfs_file_direct_read/enable
The nfsd_write_direct trace event shows how NFSD splits a given
misaligned WRITE into a DIO-aligned middle segment.
This combination of trace events is useful for WRITEs::
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_write_opened/enable
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_write_direct/enable
echo 1 > /sys/kernel/tracing/events/nfsd/nfsd_write_io_done/enable
echo 1 > /sys/kernel/tracing/events/xfs/xfs_file_direct_write/enable

View File

@@ -0,0 +1,547 @@
NFSD Maintainer Entry Profile
=============================
A Maintainer Entry Profile supplements the top-level process
documents (found in Documentation/process/) with customs that are
specific to a subsystem and its maintainers. A contributor may use
this document to set their expectations and avoid common mistakes.
A maintainer may use these profiles to look across subsystems for
opportunities to converge on best common practices.
Overview
--------
The Network File System (NFS) is a standardized family of network
protocols that enable access to files across a set of network-
connected peer hosts. Applications on NFS clients access files that
reside on file systems that are shared by NFS servers. A single
network peer can act as both an NFS client and an NFS server.
NFSD refers to the NFS server implementation included in the Linux
kernel. An in-kernel NFS server has fast access to files stored
in file systems local to that server. NFSD can share files stored
on most of the file system types native to Linux, including xfs,
ext4, btrfs, and tmpfs.
Mailing list
------------
The linux-nfs@vger.kernel.org mailing list is a public list. Its
purpose is to enable collaboration among developers working on the
Linux NFS stack, both client and server. It is not a place for
conversations that are not related directly to the Linux NFS stack.
The linux-nfs mailing list is archived on `lore.kernel.org <https://lore.kernel.org/linux-nfs/>`_.
The Linux NFS community does not have any chat room.
Reporting bugs
--------------
If you experience an NFSD-related bug on a distribution-built
kernel, please start by working with your Linux distributor.
Bug reports against upstream Linux code bases are welcome on the
linux-nfs@vger.kernel.org mailing list, where some active triage
can be done. NFSD bugs may also be reported in the Linux kernel
community's bugzilla at:
https://bugzilla.kernel.org
Please file NFSD-related bugs under the "Filesystems/NFSD"
component. In general, including as much detail as possible is a
good start, including pertinent system log messages from both
the client and server.
User space software related to NFSD, such as mountd or the exportfs
command, is contained in the nfs-utils package. Report problems
with those components to linux-nfs@vger.kernel.org. You might be
directed to move the report to a specific bug tracker.
Contributor's Guide
-------------------
Standards compliance
~~~~~~~~~~~~~~~~~~~~
The priority is for NFSD to interoperate fully with the Linux NFS
client. We also test against other popular NFS client implementa-
tions regularly at NFS bake-a-thon events (also known as plug-
fests). Non-Linux NFS clients are not part of upstream NFSD CI/CD.
The NFSD community strives to provide an NFS server implementation
that interoperates with all standards-compliant NFS client
implementations. This is done by staying as close as is sensible to
the normative mandates in the IETF's published NFS, RPC, and GSS-API
standards.
It is always useful to reference an RFC and section number in a code
comment where behavior deviates from the standard (and even when the
behavior is compliant but the implementation is obfuscatory).
On the rare occasion when a deviation from standard-mandated
behavior is needed, brief documentation of the use case or
deficiencies in the standard is a required part of in-code
documentation.
Care must always be taken to avoid leaking local error codes (ie,
errnos) to clients of NFSD. A proper NFS status code is always
required in NFS protocol replies.
NFSD administrative interfaces
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NFSD administrative interfaces include:
- an NFSD or SUNRPC module parameter
- export options in /etc/exports
- files under /proc/fs/nfsd/ or /proc/sys/sunrpc/
- the NFSD netlink protocol
Frequently, a request is made to introduce or modify one of NFSD's
traditional administrative interfaces. Certainly it is technically
easy to introduce a new administrative setting. However, there are
good reasons why the NFSD maintainers prefer to leave that as a last
resort:
- As with any API, administrative interfaces are difficult to get
right.
- Once they are documented and have a legacy of use, administrative
interfaces become difficult to modify or remove.
- Every new administrative setting multiplies the NFSD test matrix.
- The cost of one administrative interface is incremental, but costs
add up across all of the existing interfaces.
It is often better for everyone if effort is made up front to
understanding the underlying requirement of the new setting, and
then trying to make it tune itself (or to become otherwise
unnecessary).
If a new setting is indeed necessary, first consider adding it to
the NFSD netlink protocol. Or if it doesn't need to be a reliable
long term user space feature, it can be added to NFSD's menagerie of
experimental settings which reside under /sys/kernel/debug/nfsd/ .
Field observability
~~~~~~~~~~~~~~~~~~~
NFSD employs several different mechanisms for observing operation,
including counters, printks, WARNings, and static trace points. Each
have their strengths and weaknesses. Contributors should select the
most appropriate tool for their task.
- BUG must be avoided if at all possible, as it will frequently
result in a full system crash.
- WARN is appropriate only when a full stack trace is useful.
- printk can show detailed information. These must not be used
in code paths where they can be triggered repeatedly by remote
users.
- dprintk can show detailed information, but can be enabled only
in pre-set groups. The overhead of emitting output makes dprintk
inappropriate for frequent operations like I/O.
- Counters are always on, but provide little information about
individual events other than how frequently they occur.
- static trace points can be enabled individually or in groups
(via a glob). These are generally low overhead, and thus are
favored for use in hot paths.
- dynamic tracing, such as kprobes or eBPF, are quite flexible but
cannot be used in certain environments (eg, full kernel lock-
down).
Testing
~~~~~~~
The kdevops project
https://github.com/linux-kdevops/kdevops
contains several NFS-specific workflows, as well as the community
standard fstests suite. These workflows are based on open source
testing tools such as ltp and fio. Contributors are encouraged to
use these tools without kdevops, or contributors should install and
use kdevops themselves to verify their patches before submission.
Coding style
~~~~~~~~~~~~
Follow the coding style preferences described in
Documentation/process/coding-style.rst
with the following exceptions:
- Add new local variables to a function in reverse Christmas tree
order
- Use the kdoc comment style for
+ non-static functions
+ static inline functions
+ static functions that are callbacks/virtual functions
- All new function names start with ``nfsd_`` for non-NFS-version-
specific functions.
- New function names that are specific to NFSv2 or NFSv3, or are
used by all minor versions of NFSv4, use ``nfsdN_`` where N is
the version.
- New function names specific to an NFSv4 minor version can be
named with ``nfsd4M_`` where M is the minor version.
Patch preparation
~~~~~~~~~~~~~~~~~
Read and follow all guidelines in
Documentation/process/submitting-patches.rst
Use tagging to identify all patch authors. However, reviewers and
testers should be added by replying to the email patch submission.
Email is extensively used in order to publicly archive review and
testing attributions. These tags are automatically inserted into
your patches when they are applied.
The code in the body of the diff already shows /what/ is being
changed. Thus it is not necessary to repeat that in the patch
description. Instead, the description should contain one or more
of:
- A brief problem statement ("what is this patch trying to fix?")
with a root-cause analysis.
- End-user visible symptoms or items that a support engineer might
use to search for the patch, like stack traces.
- A brief explanation of why the patch is the best way to address
the problem.
- Any context that reviewers might need to understand the changes
made by the patch.
- Any relevant benchmarking results, and/or functional test results.
As detailed in Documentation/process/submitting-patches.rst,
identify the point in history that the issue being addressed was
introduced by using a Fixes: tag.
Mention in the patch description if that point in history cannot be
determined -- that is, no Fixes: tag can be provided. In this case,
please make it clear to maintainers whether an LTS backport is
needed even though there is no Fixes: tag.
The NFSD maintainers prefer to add stable tagging themselves, after
public discussion in response to the patch submission. Contributors
may suggest stable tagging, but be aware that many version
management tools add such stable Cc's when you post your patches.
Don't add "Cc: stable" unless you are absolutely sure the patch
needs to go to stable during the initial submission process.
Patch submission
~~~~~~~~~~~~~~~~
Patches to NFSD are submitted via the kernel's email-based review
process that is common to most other kernel subsystems.
Just before each submission, rebase your patch or series on the
nfsd-testing branch at
https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
The NFSD subsystem is maintained separately from the Linux in-kernel
NFS client. The NFSD maintainers do not normally take submissions
for client changes, nor can they respond authoritatively to bug
reports or feature requests for NFS client code.
This means that contributors might be asked to resubmit patches if
they were emailed to the incorrect set of maintainers and reviewers.
This is not a rejection, but simply a correction of the submission
process.
When in doubt, consult the NFSD entry in the MAINTAINERS file to
see which files and directories fall under the NFSD subsystem.
The proper set of email addresses for NFSD patches are:
To: the NFSD maintainers and reviewers listed in MAINTAINERS
Cc: linux-nfs@vger.kernel.org and optionally linux-kernel@
If there are other subsystems involved in the patches (for example
MM or RDMA) their primary mailing list address can be included in
the Cc: field. Other contributors and interested parties may be
included there as well.
In general we prefer that contributors use common patch email tools
such as "git send-email" or "stg email format/send", which tend to
get the details right without a lot of fuss.
A series consisting of a single patch is not required to have a
cover letter. However, a cover letter can be included if there is
substantial context that is not appropriate to include in the
patch description.
Please note that, with an e-mail based submission process, series
cover letters are not part of the work that is committed to the
kernel source code base or its commit history. Therefore always try
to keep pertinent information in the patch descriptions.
Design documentation is welcome, but as cover letters are not
preserved, a perhaps better option is to include a patch that adds
such documentation under Documentation/filesystems/nfs/.
Reviewers will ask about test coverage and what use cases the
patches are expected to address. Please be prepared to answer these
questions.
Review comments from maintainers might be politely stated, but in
general, these are not optional to address when they are actionable.
If necessary, the maintainers retain the right to not apply patches
when contributors refuse to address reasonable requests.
Post changes to kernel source code and user space source code as
separate series. You can connect the two series with comments in
your cover letters.
Generally the NFSD maintainers ask for a reposts even for simple
modifications in order to publicly archive the request and the
resulting repost before it is pulled into the NFSD trees. This
also enables us to rebuild a patch series quickly without missing
changes that might have been discussed via email.
Avoid frequently reposting large series with only small changes. As
a rule of thumb, posting substantial changes more than once a week
will result in reviewer overload.
Remember, there are only a handful of subsystem maintainers and
reviewers, but potentially many sources of contributions. The
maintainers and reviewers, therefore, are always the less scalable
resource. Be kind to your friendly neighborhood maintainer.
Patch Acceptance
~~~~~~~~~~~~~~~~
There isn't a formal review process for NFSD, but we like to see
at least two Reviewed-by: notices for patches that are more than
simple clean-ups. Reviews are done in public on
linux-nfs@vger.kernel.org and are archived on lore.kernel.org.
Currently the NFSD patch queues are maintained in branches here:
https://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
The NFSD maintainers apply patches initially to the nfsd-testing
branch, which is always open to new submissions. Patches can be
applied while review is ongoing. nfsd-testing is a topic branch,
so it can change frequently, it will be rebased, and your patch
might get dropped if there is a problem with it.
Generally a script-generated "thank you" email will indicate when
your patch has been added to the nfsd-testing branch. You can track
the progress of your patch using the linux-nfs patchworks instance:
https://patchwork.kernel.org/project/linux-nfs/list/
While your patch is in nfsd-testing, it is exposed to a variety of
test environments, including community zero-day bots, static
analysis tools, and NFSD continuous integration testing. The soak
period is three to four weeks.
Each patch that survives in nfsd-testing for the soak period without
changes is moved to the nfsd-next branch.
The nfsd-next branch is automatically merged into linux-next and
fs-next on a nightly basis.
Patches that survive in nfsd-next are included in the next NFSD
merge window pull request. These windows typically occur once every
63 days (nine weeks).
When the upstream merge window closes, the nfsd-next branch is
renamed nfsd-fixes, and a new nfsd-next branch is created, based on
the upstream -rc1 tag.
Fixes that are destined for an upstream -rc release also run the
nfsd-testing gauntlet, but are then applied to the nfsd-fixes
branch. That branch is made available for Linus to pull after a
short time. In order to limit the risk of introducing regressions,
we limit such fixes to emergency situations or fixes to breakage
that occurred during the most recent upstream merge.
Please make it clear when submitting an emergency patch that
immediate action (either application to -rc or LTS backport) is
needed.
Sensitive patch submissions and bug reports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CVEs are generated by specific members of the Linux kernel community
and several external entities. The Linux NFS community does not emit
or assign CVEs. CVEs are assigned after an issue and its fix are
known.
However, the NFSD maintainers sometimes receive sensitive security
reports, and at times these are significant enough to need to be
embargoed. In such rare cases, fixes can be developed and reviewed
out of the public eye.
Please be aware that many version management tools add the stable
Cc's when you post your patches. This is generally a nuisance, but
it can result in outing an embargoed security issue accidentally.
Don't add "Cc: stable" unless you are absolutely sure the patch
needs to go to stable@ during the initial submission process.
Patches that are merged without ever appearing on any list, and
which carry a Reported-by: or Fixes: tag are detected as suspicious
by security-focused people. We encourage that, after any private
review, security-sensitive patches should be posted to linux-nfs@
for the usual public review, archiving, and test period.
LLM-generated submissions
~~~~~~~~~~~~~~~~~~~~~~~~~
The Linux kernel community as a whole is still exploring the new
world of LLM-generated code. The NFSD maintainers will entertain
submission of patches that are partially or wholly generated by
LLM-based development tools. Such submissions are held to the
same standards as submissions created entirely by human authors:
- The human contributor identifies themselves via a Signed-off-by:
tag. This tag counts as a DoC.
- The human contributor is solely responsible for code provenance
and any contamination by inadvertently-included code with a
conflicting license, as usual.
- The human contributor must be able to answer and address review
questions. A patch description such as "This fixed my problem
but I don't know why" is not acceptable.
- The contribution is subjected to the same test regimen as all
other submissions.
- An indication (via a Generated-by: tag or otherwise) that the
contribution is LLM-generated is not required.
It is easy to address review comments and fix requests in LLM
generated code. So easy, in fact, that it becomes tempting to repost
refreshed code immediately. Please resist that temptation.
As always, please avoid reposting series revisions more than once
every 24 hours.
Clean-up patches
~~~~~~~~~~~~~~~~
The NFSD maintainers discourage patches which perform simple clean-
ups, which are not in the context of other work. For example:
* Addressing ``checkpatch.pl`` warnings after merge
* Addressing :ref:`Local variable ordering<rcs>` issues
* Addressing long-standing whitespace damage
This is because it is felt that the churn that such changes produce
comes at a greater cost than the value of such clean-ups.
Conversely, spelling and grammar fixes are encouraged.
Stable and LTS support
----------------------
Upstream NFSD continuous integration testing runs against LTS trees
whenever they are updated.
Please indicate when a patch containing a fix needs to be considered
for LTS kernels, either via a Fixes: tag or explicit mention.
Feature requests
----------------
There is no one way to make an official feature request, but
discussion about the request should eventually make its way to
the linux-nfs@vger.kernel.org mailing list for public review by
the community.
Subsystem boundaries
~~~~~~~~~~~~~~~~~~~~
NFSD itself is not much more than a protocol engine. This means its
primary responsibility is to translate the NFS protocol into API
calls in the Linux kernel. For example, NFSD is not responsible for
knowing exactly how bytes or file attributes are managed on a block
device. It relies on other kernel subsystems for that.
If the subsystems on which NFSD relies do not implement a particular
feature, even if the standard NFS protocols do support that feature,
that usually means NFSD cannot provide that feature without
substantial development work in other areas of the kernel.
Specificity
~~~~~~~~~~~
Feature requests can come from anywhere, and thus can often be
nebulous. A requester might not understand what a "use case" or
"user story" is. These descriptive paradigms are often used by
developers and architects to understand what is required of a
design, but are terms of art in the software trade, not used in
the everyday world.
In order to prevent contributors and maintainers from becoming
overwhelmed, we won't be afraid of saying "no" politely to
underspecified requests.
Community roles and their authority
-----------------------------------
The purpose of Linux subsystem communities is to provide expertise
and active stewardship of a narrow set of source files in the Linux
kernel. This can include managing user space tooling as well.
To contextualize the structure of the Linux NFS community that
is responsible for stewardship of the NFS server code base, we
define the community roles here.
- **Contributor** : Anyone who submits a code change, bug fix,
recommendation, documentation fix, and so on. A contributor can
submit regularly or infrequently.
- **Outside Contributor** : A contributor who is not a regular actor
in the Linux NFS community. This can mean someone who contributes
to other parts of the kernel, or someone who just noticed a
misspelling in a comment and sent a patch.
- **Reviewer** : Someone who is named in the MAINTAINERS file as a
reviewer is an area expert who can request changes to contributed
code, and expects that contributors will address the request.
- **External Reviewer** : Someone who is not named in the
MAINTAINERS file as a reviewer, but who is an area expert.
Examples include Linux kernel contributors with networking,
security, or persistent storage expertise, or developers who
contribute primarily to other NFS implementations.
One or more people will take on the following roles. These people
are often generically referred to as "maintainers", and are
identified in the MAINTAINERS file with the "M:" tag under the NFSD
subsystem.
- **Upstream Release Manager** : This role is responsible for
curating contributions into a branch, reviewing test results, and
then sending a pull request during merge windows. There is a
trust relationship between the release manager and Linus.
- **Bug Triager** : Someone who is a first responder to bug reports
submitted to the linux-nfs mailing list or bug trackers, and helps
troubleshoot and identify next steps.
- **Security Lead** : The security lead handles contacts from the
security community to resolve immediate issues, as well as dealing
with long-term security issues such as supply chain concerns. For
upstream, that's usually whether contributions violate licensing
or other intellectual property agreements.
- **Testing Lead** : The testing lead builds and runs the test
infrastructure for the subsystem. The testing lead may ask for
patches to be dropped because of ongoing high defect rates.
- **LTS Maintainer** : The LTS maintainer is responsible for managing
the Fixes: and Cc: stable annotations on patches, and seeing that
patches that cannot be automatically applied to LTS kernels get
proper manual backports as necessary.
- **Community Manager** : This umpire role can be asked to call balls
and strikes during conflicts, but is also responsible for ensuring
the health of the relationships within the community and for
facilitating discussions on long-term topics such as how to manage
growing technical debt.

View File

@@ -110,5 +110,6 @@ to do something different in the near future.
../process/maintainer-netdev
../driver-api/vfio-pci-device-specific-driver-acceptance
../nvme/feature-and-quirk-policy
../filesystems/nfs/nfsd-maintainer-entry-profile
../filesystems/xfs/xfs-maintainer-entry-profile
../mm/damon/maintainer-profile

View File

@@ -7,7 +7,7 @@ Landlock LSM: kernel documentation
==================================
:Author: Mickaël Salaün
:Date: March 2025
:Date: September 2025
Landlock's goal is to create scoped access-control (i.e. sandboxing). To
harden a whole system, this feature should be available to any process,
@@ -110,6 +110,12 @@ Filesystem
.. kernel-doc:: security/landlock/fs.h
:identifiers:
Process credential
------------------
.. kernel-doc:: security/landlock/cred.h
:identifiers:
Ruleset and domain
------------------
@@ -128,6 +134,9 @@ makes the reasoning much easier and helps avoid pitfalls.
.. kernel-doc:: security/landlock/ruleset.h
:identifiers:
.. kernel-doc:: security/landlock/domain.h
:identifiers:
Additional documentation
========================

View File

@@ -4432,6 +4432,7 @@ F: arch/*/lib/bitops.c
F: include/asm-generic/bitops
F: include/asm-generic/bitops.h
F: include/linux/bitops.h
F: lib/hweight.c
F: lib/test_bitops.c
F: tools/*/bitops*
@@ -13653,6 +13654,7 @@ R: Dai Ngo <Dai.Ngo@oracle.com>
R: Tom Talpey <tom@talpey.com>
L: linux-nfs@vger.kernel.org
S: Supported
P: Documentation/filesystems/nfs/nfsd-maintainer-entry-profile.rst
B: https://bugzilla.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
F: Documentation/filesystems/nfs/
@@ -13672,6 +13674,10 @@ F: include/uapi/linux/sunrpc/
F: net/sunrpc/
F: tools/net/sunrpc/
KERNEL NFSD BLOCK and SCSI LAYOUT DRIVER
R: Christoph Hellwig <hch@lst.de>
F: fs/nfsd/blocklayout*
KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD)
M: Thomas Weißschuh <linux@weissschuh.net>
R: Christian Heusel <christian@heusel.eu>
@@ -17522,6 +17528,7 @@ M: Luis Chamberlain <mcgrof@kernel.org>
M: Petr Pavlu <petr.pavlu@suse.com>
M: Daniel Gomez <da.gomez@kernel.org>
R: Sami Tolvanen <samitolvanen@google.com>
R: Aaron Tomlin <atomlin@atomlin.com>
L: linux-modules@vger.kernel.org
L: linux-kernel@vger.kernel.org
S: Maintained
@@ -17531,6 +17538,8 @@ F: include/linux/module*.h
F: kernel/module/
F: lib/test_kmod.c
F: lib/tests/module/
F: rust/kernel/module_param.rs
F: rust/macros/module.rs
F: scripts/module*
F: tools/testing/selftests/kmod/
F: tools/testing/selftests/module/
@@ -20089,6 +20098,7 @@ Q: https://patchwork.kernel.org/project/linux-pci/list/
B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/ABI/testing/sysfs-devices-pci-host-bridge
F: Documentation/PCI/
F: Documentation/devicetree/bindings/pci/
F: arch/x86/kernel/early-quirks.c
@@ -26388,14 +26398,16 @@ M: David Lechner <dlechner@baylibre.com>
S: Maintained
F: Documentation/devicetree/bindings/trigger-source/*
TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE
TRUSTED EXECUTION ENVIRONMENT SECURITY MANAGER (TSM)
M: Dan Williams <dan.j.williams@intel.com>
L: linux-coco@lists.linux.dev
S: Maintained
F: Documentation/ABI/testing/configfs-tsm-report
F: Documentation/driver-api/coco/
F: Documentation/driver-api/pci/tsm.rst
F: drivers/pci/tsm.c
F: drivers/virt/coco/guest/
F: include/linux/tsm*.h
F: include/linux/*tsm*.h
F: samples/tsm-mr/
TRUSTED SERVICES TEE DRIVER

View File

@@ -224,28 +224,26 @@ static int pci_dac_dma_supported(struct pci_dev *dev, u64 mask)
until either pci_unmap_single or pci_dma_sync_single is performed. */
static dma_addr_t
pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size,
pci_map_single_1(struct pci_dev *pdev, phys_addr_t paddr, size_t size,
int dac_allowed)
{
struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose;
dma_addr_t max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
unsigned long offset = offset_in_page(paddr);
struct pci_iommu_arena *arena;
long npages, dma_ofs, i;
unsigned long paddr;
dma_addr_t ret;
unsigned int align = 0;
struct device *dev = pdev ? &pdev->dev : NULL;
paddr = __pa(cpu_addr);
#if !DEBUG_NODIRECT
/* First check to see if we can use the direct map window. */
if (paddr + size + __direct_map_base - 1 <= max_dma
&& paddr + size <= __direct_map_size) {
ret = paddr + __direct_map_base;
DBGA2("pci_map_single: [%p,%zx] -> direct %llx from %ps\n",
cpu_addr, size, ret, __builtin_return_address(0));
DBGA2("pci_map_single: [%pa,%zx] -> direct %llx from %ps\n",
&paddr, size, ret, __builtin_return_address(0));
return ret;
}
@@ -255,8 +253,8 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size,
if (dac_allowed) {
ret = paddr + alpha_mv.pci_dac_offset;
DBGA2("pci_map_single: [%p,%zx] -> DAC %llx from %ps\n",
cpu_addr, size, ret, __builtin_return_address(0));
DBGA2("pci_map_single: [%pa,%zx] -> DAC %llx from %ps\n",
&paddr, size, ret, __builtin_return_address(0));
return ret;
}
@@ -290,10 +288,10 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size,
arena->ptes[i + dma_ofs] = mk_iommu_pte(paddr);
ret = arena->dma_base + dma_ofs * PAGE_SIZE;
ret += (unsigned long)cpu_addr & ~PAGE_MASK;
ret += offset;
DBGA2("pci_map_single: [%p,%zx] np %ld -> sg %llx from %ps\n",
cpu_addr, size, npages, ret, __builtin_return_address(0));
DBGA2("pci_map_single: [%pa,%zx] np %ld -> sg %llx from %ps\n",
&paddr, size, npages, ret, __builtin_return_address(0));
return ret;
}
@@ -322,19 +320,18 @@ static struct pci_dev *alpha_gendev_to_pci(struct device *dev)
return NULL;
}
static dma_addr_t alpha_pci_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
static dma_addr_t alpha_pci_map_phys(struct device *dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
struct pci_dev *pdev = alpha_gendev_to_pci(dev);
int dac_allowed;
BUG_ON(dir == DMA_NONE);
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0;
return pci_map_single_1(pdev, (char *)page_address(page) + offset,
size, dac_allowed);
dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0;
return pci_map_single_1(pdev, phys, size, dac_allowed);
}
/* Unmap a single streaming mode DMA translation. The DMA_ADDR and
@@ -343,7 +340,7 @@ static dma_addr_t alpha_pci_map_page(struct device *dev, struct page *page,
the cpu to the buffer are guaranteed to see whatever the device
wrote there. */
static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr,
static void alpha_pci_unmap_phys(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
@@ -353,8 +350,6 @@ static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr,
struct pci_iommu_arena *arena;
long dma_ofs, npages;
BUG_ON(dir == DMA_NONE);
if (dma_addr >= __direct_map_base
&& dma_addr < __direct_map_base + __direct_map_size) {
/* Nothing to do. */
@@ -429,7 +424,7 @@ try_again:
}
memset(cpu_addr, 0, size);
*dma_addrp = pci_map_single_1(pdev, cpu_addr, size, 0);
*dma_addrp = pci_map_single_1(pdev, virt_to_phys(cpu_addr), size, 0);
if (*dma_addrp == DMA_MAPPING_ERROR) {
free_pages((unsigned long)cpu_addr, order);
if (alpha_mv.mv_pci_tbi || (gfp & GFP_DMA))
@@ -643,9 +638,8 @@ static int alpha_pci_map_sg(struct device *dev, struct scatterlist *sg,
/* Fast path single entry scatterlists. */
if (nents == 1) {
sg->dma_length = sg->length;
sg->dma_address
= pci_map_single_1(pdev, SG_ENT_VIRT_ADDRESS(sg),
sg->length, dac_allowed);
sg->dma_address = pci_map_single_1(pdev, sg_phys(sg),
sg->length, dac_allowed);
if (sg->dma_address == DMA_MAPPING_ERROR)
return -EIO;
return 1;
@@ -917,8 +911,8 @@ iommu_unbind(struct pci_iommu_arena *arena, long pg_start, long pg_count)
const struct dma_map_ops alpha_pci_ops = {
.alloc = alpha_pci_alloc_coherent,
.free = alpha_pci_free_coherent,
.map_page = alpha_pci_map_page,
.unmap_page = alpha_pci_unmap_page,
.map_phys = alpha_pci_map_phys,
.unmap_phys = alpha_pci_unmap_phys,
.map_sg = alpha_pci_map_sg,
.unmap_sg = alpha_pci_unmap_sg,
.dma_supported = alpha_pci_supported,

View File

@@ -624,16 +624,14 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
kfree(buf);
}
static void dma_cache_maint_page(struct page *page, unsigned long offset,
size_t size, enum dma_data_direction dir,
static void dma_cache_maint_page(phys_addr_t phys, size_t size,
enum dma_data_direction dir,
void (*op)(const void *, size_t, int))
{
unsigned long pfn;
unsigned long offset = offset_in_page(phys);
unsigned long pfn = __phys_to_pfn(phys);
size_t left = size;
pfn = page_to_pfn(page) + offset / PAGE_SIZE;
offset %= PAGE_SIZE;
/*
* A single sg entry may refer to multiple physically contiguous
* pages. But we still need to process highmem pages individually.
@@ -644,17 +642,18 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
size_t len = left;
void *vaddr;
page = pfn_to_page(pfn);
if (PageHighMem(page)) {
phys = __pfn_to_phys(pfn);
if (PhysHighMem(phys)) {
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
if (cache_is_vipt_nonaliasing()) {
vaddr = kmap_atomic(page);
vaddr = kmap_atomic_pfn(pfn);
op(vaddr + offset, len, dir);
kunmap_atomic(vaddr);
} else {
struct page *page = phys_to_page(phys);
vaddr = kmap_high_get(page);
if (vaddr) {
op(vaddr + offset, len, dir);
@@ -662,7 +661,8 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
}
}
} else {
vaddr = page_address(page) + offset;
phys += offset;
vaddr = phys_to_virt(phys);
op(vaddr, len, dir);
}
offset = 0;
@@ -676,14 +676,11 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset,
* Note: Drivers should NOT use this function directly.
* Use the driver DMA support - see dma-mapping.h (dma_sync_*)
*/
static void __dma_page_cpu_to_dev(struct page *page, unsigned long off,
size_t size, enum dma_data_direction dir)
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
phys_addr_t paddr;
dma_cache_maint_page(paddr, size, dir, dmac_map_area);
dma_cache_maint_page(page, off, size, dir, dmac_map_area);
paddr = page_to_phys(page) + off;
if (dir == DMA_FROM_DEVICE) {
outer_inv_range(paddr, paddr + size);
} else {
@@ -692,17 +689,15 @@ static void __dma_page_cpu_to_dev(struct page *page, unsigned long off,
/* FIXME: non-speculating: flush on bidirectional mappings? */
}
static void __dma_page_dev_to_cpu(struct page *page, unsigned long off,
size_t size, enum dma_data_direction dir)
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
phys_addr_t paddr = page_to_phys(page) + off;
/* FIXME: non-speculating: not required */
/* in any case, don't bother invalidating if DMA to device */
if (dir != DMA_TO_DEVICE) {
outer_inv_range(paddr, paddr + size);
dma_cache_maint_page(page, off, size, dir, dmac_unmap_area);
dma_cache_maint_page(paddr, size, dir, dmac_unmap_area);
}
/*
@@ -737,6 +732,9 @@ static int __dma_info_to_prot(enum dma_data_direction dir, unsigned long attrs)
if (attrs & DMA_ATTR_PRIVILEGED)
prot |= IOMMU_PRIV;
if (attrs & DMA_ATTR_MMIO)
prot |= IOMMU_MMIO;
switch (dir) {
case DMA_BIDIRECTIONAL:
return prot | IOMMU_READ | IOMMU_WRITE;
@@ -1205,7 +1203,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
unsigned int len = PAGE_ALIGN(s->offset + s->length);
if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
arch_sync_dma_for_device(sg_phys(s), s->length, dir);
prot = __dma_info_to_prot(dir, attrs);
@@ -1307,8 +1305,7 @@ static void arm_iommu_unmap_sg(struct device *dev,
__iommu_remove_mapping(dev, sg_dma_address(s),
sg_dma_len(s));
if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
__dma_page_dev_to_cpu(sg_page(s), s->offset,
s->length, dir);
arch_sync_dma_for_cpu(sg_phys(s), s->length, dir);
}
}
@@ -1330,7 +1327,7 @@ static void arm_iommu_sync_sg_for_cpu(struct device *dev,
return;
for_each_sg(sg, s, nents, i)
__dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
arch_sync_dma_for_cpu(sg_phys(s), s->length, dir);
}
@@ -1352,29 +1349,31 @@ static void arm_iommu_sync_sg_for_device(struct device *dev,
return;
for_each_sg(sg, s, nents, i)
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
arch_sync_dma_for_device(sg_phys(s), s->length, dir);
}
/**
* arm_iommu_map_page
* arm_iommu_map_phys
* @dev: valid struct device pointer
* @page: page that buffer resides in
* @offset: offset into page for start of buffer
* @phys: physical address that buffer resides in
* @size: size of buffer to map
* @dir: DMA transfer direction
* @attrs: DMA mapping attributes
*
* IOMMU aware version of arm_dma_map_page()
*/
static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs)
static dma_addr_t arm_iommu_map_phys(struct device *dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
int len = PAGE_ALIGN(size + offset_in_page(phys));
phys_addr_t addr = phys & PAGE_MASK;
dma_addr_t dma_addr;
int ret, prot, len = PAGE_ALIGN(size + offset);
int ret, prot;
if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
__dma_page_cpu_to_dev(page, offset, size, dir);
if (!dev->dma_coherent &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
arch_sync_dma_for_device(phys, size, dir);
dma_addr = __alloc_iova(mapping, len);
if (dma_addr == DMA_MAPPING_ERROR)
@@ -1382,12 +1381,11 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
prot = __dma_info_to_prot(dir, attrs);
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len,
prot, GFP_KERNEL);
ret = iommu_map(mapping->domain, dma_addr, addr, len, prot, GFP_KERNEL);
if (ret < 0)
goto fail;
return dma_addr + offset;
return dma_addr + offset_in_page(phys);
fail:
__free_iova(mapping, dma_addr, len);
return DMA_MAPPING_ERROR;
@@ -1399,100 +1397,45 @@ fail:
* @handle: DMA address of buffer
* @size: size of buffer (same as passed to dma_map_page)
* @dir: DMA transfer direction (same as passed to dma_map_page)
* @attrs: DMA mapping attributes
*
* IOMMU aware version of arm_dma_unmap_page()
* IOMMU aware version of arm_dma_unmap_phys()
*/
static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
static void arm_iommu_unmap_phys(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
dma_addr_t iova = handle & PAGE_MASK;
struct page *page;
int offset = handle & ~PAGE_MASK;
int len = PAGE_ALIGN(size + offset);
if (!iova)
return;
if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
__dma_page_dev_to_cpu(page, offset, size, dir);
if (!dev->dma_coherent &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
phys_addr_t phys = iommu_iova_to_phys(mapping->domain, iova);
arch_sync_dma_for_cpu(phys + offset, size, dir);
}
iommu_unmap(mapping->domain, iova, len);
__free_iova(mapping, iova, len);
}
/**
* arm_iommu_map_resource - map a device resource for DMA
* @dev: valid struct device pointer
* @phys_addr: physical address of resource
* @size: size of resource to map
* @dir: DMA transfer direction
*/
static dma_addr_t arm_iommu_map_resource(struct device *dev,
phys_addr_t phys_addr, size_t size,
enum dma_data_direction dir, unsigned long attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
dma_addr_t dma_addr;
int ret, prot;
phys_addr_t addr = phys_addr & PAGE_MASK;
unsigned int offset = phys_addr & ~PAGE_MASK;
size_t len = PAGE_ALIGN(size + offset);
dma_addr = __alloc_iova(mapping, len);
if (dma_addr == DMA_MAPPING_ERROR)
return dma_addr;
prot = __dma_info_to_prot(dir, attrs) | IOMMU_MMIO;
ret = iommu_map(mapping->domain, dma_addr, addr, len, prot, GFP_KERNEL);
if (ret < 0)
goto fail;
return dma_addr + offset;
fail:
__free_iova(mapping, dma_addr, len);
return DMA_MAPPING_ERROR;
}
/**
* arm_iommu_unmap_resource - unmap a device DMA resource
* @dev: valid struct device pointer
* @dma_handle: DMA address to resource
* @size: size of resource to map
* @dir: DMA transfer direction
*/
static void arm_iommu_unmap_resource(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
dma_addr_t iova = dma_handle & PAGE_MASK;
unsigned int offset = dma_handle & ~PAGE_MASK;
size_t len = PAGE_ALIGN(size + offset);
if (!iova)
return;
iommu_unmap(mapping->domain, iova, len);
__free_iova(mapping, iova, len);
}
static void arm_iommu_sync_single_for_cpu(struct device *dev,
dma_addr_t handle, size_t size, enum dma_data_direction dir)
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
dma_addr_t iova = handle & PAGE_MASK;
struct page *page;
unsigned int offset = handle & ~PAGE_MASK;
phys_addr_t phys;
if (dev->dma_coherent || !iova)
return;
page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
__dma_page_dev_to_cpu(page, offset, size, dir);
phys = iommu_iova_to_phys(mapping->domain, iova);
arch_sync_dma_for_cpu(phys + offset, size, dir);
}
static void arm_iommu_sync_single_for_device(struct device *dev,
@@ -1500,14 +1443,14 @@ static void arm_iommu_sync_single_for_device(struct device *dev,
{
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
dma_addr_t iova = handle & PAGE_MASK;
struct page *page;
unsigned int offset = handle & ~PAGE_MASK;
phys_addr_t phys;
if (dev->dma_coherent || !iova)
return;
page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
__dma_page_cpu_to_dev(page, offset, size, dir);
phys = iommu_iova_to_phys(mapping->domain, iova);
arch_sync_dma_for_device(phys + offset, size, dir);
}
static const struct dma_map_ops iommu_ops = {
@@ -1516,8 +1459,8 @@ static const struct dma_map_ops iommu_ops = {
.mmap = arm_iommu_mmap_attrs,
.get_sgtable = arm_iommu_get_sgtable,
.map_page = arm_iommu_map_page,
.unmap_page = arm_iommu_unmap_page,
.map_phys = arm_iommu_map_phys,
.unmap_phys = arm_iommu_unmap_phys,
.sync_single_for_cpu = arm_iommu_sync_single_for_cpu,
.sync_single_for_device = arm_iommu_sync_single_for_device,
@@ -1525,9 +1468,6 @@ static const struct dma_map_ops iommu_ops = {
.unmap_sg = arm_iommu_unmap_sg,
.sync_sg_for_cpu = arm_iommu_sync_sg_for_cpu,
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
.map_resource = arm_iommu_map_resource,
.unmap_resource = arm_iommu_unmap_resource,
};
/**
@@ -1794,20 +1734,6 @@ void arch_teardown_dma_ops(struct device *dev)
set_dma_ops(dev, NULL);
}
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
__dma_page_cpu_to_dev(phys_to_page(paddr), paddr & (PAGE_SIZE - 1),
size, dir);
}
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
__dma_page_dev_to_cpu(phys_to_page(paddr), paddr & (PAGE_SIZE - 1),
size, dir);
}
void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp, unsigned long attrs)
{

View File

@@ -521,18 +521,24 @@ static void jazz_dma_free(struct device *dev, size_t size, void *vaddr,
__free_pages(virt_to_page(vaddr), get_order(size));
}
static dma_addr_t jazz_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs)
static dma_addr_t jazz_dma_map_phys(struct device *dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
phys_addr_t phys = page_to_phys(page) + offset;
if (unlikely(attrs & DMA_ATTR_MMIO))
/*
* This check is included because older versions of the code lacked
* MMIO path support, and my ability to test this path is limited.
* However, from a software technical standpoint, there is no restriction,
* as the following code operates solely on physical addresses.
*/
return DMA_MAPPING_ERROR;
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_device(phys, size, dir);
return vdma_alloc(phys, size);
}
static void jazz_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
static void jazz_dma_unmap_phys(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
@@ -607,8 +613,8 @@ static void jazz_dma_sync_sg_for_cpu(struct device *dev,
const struct dma_map_ops jazz_dma_ops = {
.alloc = jazz_dma_alloc,
.free = jazz_dma_free,
.map_page = jazz_dma_map_page,
.unmap_page = jazz_dma_unmap_page,
.map_phys = jazz_dma_map_phys,
.unmap_phys = jazz_dma_unmap_phys,
.map_sg = jazz_dma_map_sg,
.unmap_sg = jazz_dma_unmap_sg,
.sync_single_for_cpu = jazz_dma_sync_single_for_cpu,

View File

@@ -274,12 +274,12 @@ extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
unsigned long mask, gfp_t flag, int node);
extern void iommu_free_coherent(struct iommu_table *tbl, size_t size,
void *vaddr, dma_addr_t dma_handle);
extern dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
struct page *page, unsigned long offset,
size_t size, unsigned long mask,
extern dma_addr_t iommu_map_phys(struct device *dev, struct iommu_table *tbl,
phys_addr_t phys, size_t size,
unsigned long mask,
enum dma_data_direction direction,
unsigned long attrs);
extern void iommu_unmap_page(struct iommu_table *tbl, dma_addr_t dma_handle,
extern void iommu_unmap_phys(struct iommu_table *tbl, dma_addr_t dma_handle,
size_t size, enum dma_data_direction direction,
unsigned long attrs);

View File

@@ -93,28 +93,26 @@ static void dma_iommu_free_coherent(struct device *dev, size_t size,
/* Creates TCEs for a user provided buffer. The user buffer must be
* contiguous real kernel storage (not vmalloc). The address passed here
* comprises a page address and offset into that page. The dma_addr_t
* returned will point to the same byte within the page as was passed in.
* is a physical address to that page. The dma_addr_t returned will point
* to the same byte within the page as was passed in.
*/
static dma_addr_t dma_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
static dma_addr_t dma_iommu_map_phys(struct device *dev, phys_addr_t phys,
size_t size,
enum dma_data_direction direction,
unsigned long attrs)
{
return iommu_map_page(dev, get_iommu_table_base(dev), page, offset,
size, dma_get_mask(dev), direction, attrs);
return iommu_map_phys(dev, get_iommu_table_base(dev), phys, size,
dma_get_mask(dev), direction, attrs);
}
static void dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
static void dma_iommu_unmap_phys(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction direction,
unsigned long attrs)
{
iommu_unmap_page(get_iommu_table_base(dev), dma_handle, size, direction,
iommu_unmap_phys(get_iommu_table_base(dev), dma_handle, size, direction,
attrs);
}
static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction direction,
unsigned long attrs)
@@ -211,8 +209,8 @@ const struct dma_map_ops dma_iommu_ops = {
.map_sg = dma_iommu_map_sg,
.unmap_sg = dma_iommu_unmap_sg,
.dma_supported = dma_iommu_dma_supported,
.map_page = dma_iommu_map_page,
.unmap_page = dma_iommu_unmap_page,
.map_phys = dma_iommu_map_phys,
.unmap_phys = dma_iommu_unmap_phys,
.get_required_mask = dma_iommu_get_required_mask,
.mmap = dma_common_mmap,
.get_sgtable = dma_common_get_sgtable,

View File

@@ -848,12 +848,12 @@ EXPORT_SYMBOL_GPL(iommu_tce_table_put);
/* Creates TCEs for a user provided buffer. The user buffer must be
* contiguous real kernel storage (not vmalloc). The address passed here
* comprises a page address and offset into that page. The dma_addr_t
* returned will point to the same byte within the page as was passed in.
* is physical address into that page. The dma_addr_t returned will point
* to the same byte within the page as was passed in.
*/
dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
struct page *page, unsigned long offset, size_t size,
unsigned long mask, enum dma_data_direction direction,
dma_addr_t iommu_map_phys(struct device *dev, struct iommu_table *tbl,
phys_addr_t phys, size_t size, unsigned long mask,
enum dma_data_direction direction,
unsigned long attrs)
{
dma_addr_t dma_handle = DMA_MAPPING_ERROR;
@@ -863,7 +863,7 @@ dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
BUG_ON(direction == DMA_NONE);
vaddr = page_address(page) + offset;
vaddr = phys_to_virt(phys);
uaddr = (unsigned long)vaddr;
if (tbl) {
@@ -890,7 +890,7 @@ dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
return dma_handle;
}
void iommu_unmap_page(struct iommu_table *tbl, dma_addr_t dma_handle,
void iommu_unmap_phys(struct iommu_table *tbl, dma_addr_t dma_handle,
size_t size, enum dma_data_direction direction,
unsigned long attrs)
{

View File

@@ -551,18 +551,20 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
/* Creates TCEs for a user provided buffer. The user buffer must be
* contiguous real kernel storage (not vmalloc). The address passed here
* comprises a page address and offset into that page. The dma_addr_t
* returned will point to the same byte within the page as was passed in.
* is physical address to that hat page. The dma_addr_t returned will point
* to the same byte within the page as was passed in.
*/
static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction direction,
unsigned long attrs)
static dma_addr_t ps3_sb_map_phys(struct device *_dev, phys_addr_t phys,
size_t size, enum dma_data_direction direction, unsigned long attrs)
{
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
int result;
dma_addr_t bus_addr;
void *ptr = page_address(page) + offset;
void *ptr = phys_to_virt(phys);
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size,
&bus_addr,
@@ -577,8 +579,8 @@ static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
return bus_addr;
}
static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
unsigned long offset, size_t size,
static dma_addr_t ps3_ioc0_map_phys(struct device *_dev, phys_addr_t phys,
size_t size,
enum dma_data_direction direction,
unsigned long attrs)
{
@@ -586,7 +588,10 @@ static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
int result;
dma_addr_t bus_addr;
u64 iopte_flag;
void *ptr = page_address(page) + offset;
void *ptr = phys_to_virt(phys);
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
iopte_flag = CBE_IOPTE_M;
switch (direction) {
@@ -613,7 +618,7 @@ static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
return bus_addr;
}
static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr,
static void ps3_unmap_phys(struct device *_dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction direction, unsigned long attrs)
{
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
@@ -690,8 +695,8 @@ static const struct dma_map_ops ps3_sb_dma_ops = {
.map_sg = ps3_sb_map_sg,
.unmap_sg = ps3_sb_unmap_sg,
.dma_supported = ps3_dma_supported,
.map_page = ps3_sb_map_page,
.unmap_page = ps3_unmap_page,
.map_phys = ps3_sb_map_phys,
.unmap_phys = ps3_unmap_phys,
.mmap = dma_common_mmap,
.get_sgtable = dma_common_get_sgtable,
.alloc_pages_op = dma_common_alloc_pages,
@@ -704,8 +709,8 @@ static const struct dma_map_ops ps3_ioc0_dma_ops = {
.map_sg = ps3_ioc0_map_sg,
.unmap_sg = ps3_ioc0_unmap_sg,
.dma_supported = ps3_dma_supported,
.map_page = ps3_ioc0_map_page,
.unmap_page = ps3_unmap_page,
.map_phys = ps3_ioc0_map_phys,
.unmap_phys = ps3_unmap_phys,
.mmap = dma_common_mmap,
.get_sgtable = dma_common_get_sgtable,
.alloc_pages_op = dma_common_alloc_pages,

View File

@@ -86,17 +86,18 @@ static void ibmebus_free_coherent(struct device *dev,
kfree(vaddr);
}
static dma_addr_t ibmebus_map_page(struct device *dev,
struct page *page,
unsigned long offset,
static dma_addr_t ibmebus_map_phys(struct device *dev, phys_addr_t phys,
size_t size,
enum dma_data_direction direction,
unsigned long attrs)
{
return (dma_addr_t)(page_address(page) + offset);
if (attrs & DMA_ATTR_MMIO)
return DMA_MAPPING_ERROR;
return (dma_addr_t)(phys_to_virt(phys));
}
static void ibmebus_unmap_page(struct device *dev,
static void ibmebus_unmap_phys(struct device *dev,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction direction,
@@ -146,8 +147,8 @@ static const struct dma_map_ops ibmebus_dma_ops = {
.unmap_sg = ibmebus_unmap_sg,
.dma_supported = ibmebus_dma_supported,
.get_required_mask = ibmebus_dma_get_required_mask,
.map_page = ibmebus_map_page,
.unmap_page = ibmebus_unmap_page,
.map_phys = ibmebus_map_phys,
.unmap_phys = ibmebus_unmap_phys,
};
static int ibmebus_match_path(struct device *dev, const void *data)

View File

@@ -512,18 +512,21 @@ static void vio_dma_iommu_free_coherent(struct device *dev, size_t size,
vio_cmo_dealloc(viodev, roundup(size, PAGE_SIZE));
}
static dma_addr_t vio_dma_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction,
unsigned long attrs)
static dma_addr_t vio_dma_iommu_map_phys(struct device *dev, phys_addr_t phys,
size_t size,
enum dma_data_direction direction,
unsigned long attrs)
{
struct vio_dev *viodev = to_vio_dev(dev);
struct iommu_table *tbl = get_iommu_table_base(dev);
dma_addr_t ret = DMA_MAPPING_ERROR;
if (unlikely(attrs & DMA_ATTR_MMIO))
return ret;
if (vio_cmo_alloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl))))
goto out_fail;
ret = iommu_map_page(dev, tbl, page, offset, size, dma_get_mask(dev),
ret = iommu_map_phys(dev, tbl, phys, size, dma_get_mask(dev),
direction, attrs);
if (unlikely(ret == DMA_MAPPING_ERROR))
goto out_deallocate;
@@ -536,7 +539,7 @@ out_fail:
return DMA_MAPPING_ERROR;
}
static void vio_dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
static void vio_dma_iommu_unmap_phys(struct device *dev, dma_addr_t dma_handle,
size_t size,
enum dma_data_direction direction,
unsigned long attrs)
@@ -544,7 +547,7 @@ static void vio_dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
struct vio_dev *viodev = to_vio_dev(dev);
struct iommu_table *tbl = get_iommu_table_base(dev);
iommu_unmap_page(tbl, dma_handle, size, direction, attrs);
iommu_unmap_phys(tbl, dma_handle, size, direction, attrs);
vio_cmo_dealloc(viodev, roundup(size, IOMMU_PAGE_SIZE(tbl)));
}
@@ -605,8 +608,8 @@ static const struct dma_map_ops vio_dma_mapping_ops = {
.free = vio_dma_iommu_free_coherent,
.map_sg = vio_dma_iommu_map_sg,
.unmap_sg = vio_dma_iommu_unmap_sg,
.map_page = vio_dma_iommu_map_page,
.unmap_page = vio_dma_iommu_unmap_page,
.map_phys = vio_dma_iommu_map_phys,
.unmap_phys = vio_dma_iommu_unmap_phys,
.dma_supported = dma_iommu_dma_supported,
.get_required_mask = dma_iommu_get_required_mask,
.mmap = dma_common_mmap,

View File

@@ -260,26 +260,35 @@ static void dma_4u_free_coherent(struct device *dev, size_t size,
free_pages((unsigned long)cpu, order);
}
static dma_addr_t dma_4u_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t sz,
enum dma_data_direction direction,
static dma_addr_t dma_4u_map_phys(struct device *dev, phys_addr_t phys,
size_t sz, enum dma_data_direction direction,
unsigned long attrs)
{
struct iommu *iommu;
struct strbuf *strbuf;
iopte_t *base;
unsigned long flags, npages, oaddr;
unsigned long i, base_paddr, ctx;
unsigned long i, ctx;
u32 bus_addr, ret;
unsigned long iopte_protection;
if (unlikely(attrs & DMA_ATTR_MMIO))
/*
* This check is included because older versions of the code
* lacked MMIO path support, and my ability to test this path
* is limited. However, from a software technical standpoint,
* there is no restriction, as the following code operates
* solely on physical addresses.
*/
goto bad_no_ctx;
iommu = dev->archdata.iommu;
strbuf = dev->archdata.stc;
if (unlikely(direction == DMA_NONE))
goto bad_no_ctx;
oaddr = (unsigned long)(page_address(page) + offset);
oaddr = (unsigned long)(phys_to_virt(phys));
npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
npages >>= IO_PAGE_SHIFT;
@@ -296,7 +305,6 @@ static dma_addr_t dma_4u_map_page(struct device *dev, struct page *page,
bus_addr = (iommu->tbl.table_map_base +
((base - iommu->page_table) << IO_PAGE_SHIFT));
ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
base_paddr = __pa(oaddr & IO_PAGE_MASK);
if (strbuf->strbuf_enabled)
iopte_protection = IOPTE_STREAMING(ctx);
else
@@ -304,8 +312,8 @@ static dma_addr_t dma_4u_map_page(struct device *dev, struct page *page,
if (direction != DMA_TO_DEVICE)
iopte_protection |= IOPTE_WRITE;
for (i = 0; i < npages; i++, base++, base_paddr += IO_PAGE_SIZE)
iopte_val(*base) = iopte_protection | base_paddr;
for (i = 0; i < npages; i++, base++, phys += IO_PAGE_SIZE)
iopte_val(*base) = iopte_protection | phys;
return ret;
@@ -383,7 +391,7 @@ do_flush_sync:
vaddr, ctx, npages);
}
static void dma_4u_unmap_page(struct device *dev, dma_addr_t bus_addr,
static void dma_4u_unmap_phys(struct device *dev, dma_addr_t bus_addr,
size_t sz, enum dma_data_direction direction,
unsigned long attrs)
{
@@ -753,8 +761,8 @@ static int dma_4u_supported(struct device *dev, u64 device_mask)
static const struct dma_map_ops sun4u_dma_ops = {
.alloc = dma_4u_alloc_coherent,
.free = dma_4u_free_coherent,
.map_page = dma_4u_map_page,
.unmap_page = dma_4u_unmap_page,
.map_phys = dma_4u_map_phys,
.unmap_phys = dma_4u_unmap_phys,
.map_sg = dma_4u_map_sg,
.unmap_sg = dma_4u_unmap_sg,
.sync_single_for_cpu = dma_4u_sync_single_for_cpu,

View File

@@ -352,9 +352,8 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
free_pages((unsigned long)cpu, order);
}
static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t sz,
enum dma_data_direction direction,
static dma_addr_t dma_4v_map_phys(struct device *dev, phys_addr_t phys,
size_t sz, enum dma_data_direction direction,
unsigned long attrs)
{
struct iommu *iommu;
@@ -362,18 +361,27 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
struct iommu_map_table *tbl;
u64 mask;
unsigned long flags, npages, oaddr;
unsigned long i, base_paddr;
unsigned long prot;
unsigned long i, prot;
dma_addr_t bus_addr, ret;
long entry;
if (unlikely(attrs & DMA_ATTR_MMIO))
/*
* This check is included because older versions of the code
* lacked MMIO path support, and my ability to test this path
* is limited. However, from a software technical standpoint,
* there is no restriction, as the following code operates
* solely on physical addresses.
*/
goto bad;
iommu = dev->archdata.iommu;
atu = iommu->atu;
if (unlikely(direction == DMA_NONE))
goto bad;
oaddr = (unsigned long)(page_address(page) + offset);
oaddr = (unsigned long)(phys_to_virt(phys));
npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
npages >>= IO_PAGE_SHIFT;
@@ -391,7 +399,6 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
bus_addr = (tbl->table_map_base + (entry << IO_PAGE_SHIFT));
ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
base_paddr = __pa(oaddr & IO_PAGE_MASK);
prot = HV_PCI_MAP_ATTR_READ;
if (direction != DMA_TO_DEVICE)
prot |= HV_PCI_MAP_ATTR_WRITE;
@@ -403,8 +410,8 @@ static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
iommu_batch_start(dev, prot, entry);
for (i = 0; i < npages; i++, base_paddr += IO_PAGE_SIZE) {
long err = iommu_batch_add(base_paddr, mask);
for (i = 0; i < npages; i++, phys += IO_PAGE_SIZE) {
long err = iommu_batch_add(phys, mask);
if (unlikely(err < 0L))
goto iommu_map_fail;
}
@@ -426,7 +433,7 @@ iommu_map_fail:
return DMA_MAPPING_ERROR;
}
static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
static void dma_4v_unmap_phys(struct device *dev, dma_addr_t bus_addr,
size_t sz, enum dma_data_direction direction,
unsigned long attrs)
{
@@ -686,8 +693,8 @@ static int dma_4v_supported(struct device *dev, u64 device_mask)
static const struct dma_map_ops sun4v_dma_ops = {
.alloc = dma_4v_alloc_coherent,
.free = dma_4v_free_coherent,
.map_page = dma_4v_map_page,
.unmap_page = dma_4v_unmap_page,
.map_phys = dma_4v_map_phys,
.unmap_phys = dma_4v_unmap_phys,
.map_sg = dma_4v_map_sg,
.unmap_sg = dma_4v_unmap_sg,
.dma_supported = dma_4v_supported,

View File

@@ -94,13 +94,14 @@ static int __init iounit_init(void)
subsys_initcall(iounit_init);
/* One has to hold iounit->lock to call this */
static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size)
static dma_addr_t iounit_get_area(struct iounit_struct *iounit,
phys_addr_t phys, int size)
{
int i, j, k, npages;
unsigned long rotor, scan, limit;
iopte_t iopte;
npages = ((vaddr & ~PAGE_MASK) + size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
npages = (offset_in_page(phys) + size + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
/* A tiny bit of magic ingredience :) */
switch (npages) {
@@ -109,7 +110,7 @@ static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long
default: i = 0x0213; break;
}
IOD(("iounit_get_area(%08lx,%d[%d])=", vaddr, size, npages));
IOD(("%s(%pa,%d[%d])=", __func__, &phys, size, npages));
next: j = (i & 15);
rotor = iounit->rotor[j - 1];
@@ -124,7 +125,8 @@ nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan);
}
i >>= 4;
if (!(i & 15))
panic("iounit_get_area: Couldn't find free iopte slots for (%08lx,%d)\n", vaddr, size);
panic("iounit_get_area: Couldn't find free iopte slots for (%pa,%d)\n",
&phys, size);
goto next;
}
for (k = 1, scan++; k < npages; k++)
@@ -132,30 +134,29 @@ nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan);
goto nexti;
iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
scan -= npages;
iopte = MKIOPTE(__pa(vaddr & PAGE_MASK));
vaddr = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + (vaddr & ~PAGE_MASK);
iopte = MKIOPTE(phys & PAGE_MASK);
phys = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + offset_in_page(phys);
for (k = 0; k < npages; k++, iopte = __iopte(iopte_val(iopte) + 0x100), scan++) {
set_bit(scan, iounit->bmap);
sbus_writel(iopte_val(iopte), &iounit->page_table[scan]);
}
IOD(("%08lx\n", vaddr));
return vaddr;
IOD(("%pa\n", &phys));
return phys;
}
static dma_addr_t iounit_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t len, enum dma_data_direction dir,
unsigned long attrs)
static dma_addr_t iounit_map_phys(struct device *dev, phys_addr_t phys,
size_t len, enum dma_data_direction dir, unsigned long attrs)
{
void *vaddr = page_address(page) + offset;
struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long ret, flags;
unsigned long flags;
dma_addr_t ret;
/* XXX So what is maxphys for us and how do drivers know it? */
if (!len || len > 256 * 1024)
return DMA_MAPPING_ERROR;
spin_lock_irqsave(&iounit->lock, flags);
ret = iounit_get_area(iounit, (unsigned long)vaddr, len);
ret = iounit_get_area(iounit, phys, len);
spin_unlock_irqrestore(&iounit->lock, flags);
return ret;
}
@@ -171,14 +172,15 @@ static int iounit_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
/* FIXME: Cache some resolved pages - often several sg entries are to the same page */
spin_lock_irqsave(&iounit->lock, flags);
for_each_sg(sgl, sg, nents, i) {
sg->dma_address = iounit_get_area(iounit, (unsigned long) sg_virt(sg), sg->length);
sg->dma_address =
iounit_get_area(iounit, sg_phys(sg), sg->length);
sg->dma_length = sg->length;
}
spin_unlock_irqrestore(&iounit->lock, flags);
return nents;
}
static void iounit_unmap_page(struct device *dev, dma_addr_t vaddr, size_t len,
static void iounit_unmap_phys(struct device *dev, dma_addr_t vaddr, size_t len,
enum dma_data_direction dir, unsigned long attrs)
{
struct iounit_struct *iounit = dev->archdata.iommu;
@@ -279,8 +281,8 @@ static const struct dma_map_ops iounit_dma_ops = {
.alloc = iounit_alloc,
.free = iounit_free,
#endif
.map_page = iounit_map_page,
.unmap_page = iounit_unmap_page,
.map_phys = iounit_map_phys,
.unmap_phys = iounit_unmap_phys,
.map_sg = iounit_map_sg,
.unmap_sg = iounit_unmap_sg,
};

View File

@@ -181,18 +181,20 @@ static void iommu_flush_iotlb(iopte_t *iopte, unsigned int niopte)
}
}
static dma_addr_t __sbus_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t len, bool per_page_flush)
static dma_addr_t __sbus_iommu_map_phys(struct device *dev, phys_addr_t paddr,
size_t len, bool per_page_flush, unsigned long attrs)
{
struct iommu_struct *iommu = dev->archdata.iommu;
phys_addr_t paddr = page_to_phys(page) + offset;
unsigned long off = paddr & ~PAGE_MASK;
unsigned long off = offset_in_page(paddr);
unsigned long npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
unsigned long pfn = __phys_to_pfn(paddr);
unsigned int busa, busa0;
iopte_t *iopte, *iopte0;
int ioptex, i;
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
/* XXX So what is maxphys for us and how do drivers know it? */
if (!len || len > 256 * 1024)
return DMA_MAPPING_ERROR;
@@ -202,10 +204,10 @@ static dma_addr_t __sbus_iommu_map_page(struct device *dev, struct page *page,
* XXX Is this a good assumption?
* XXX What if someone else unmaps it here and races us?
*/
if (per_page_flush && !PageHighMem(page)) {
if (per_page_flush && !PhysHighMem(paddr)) {
unsigned long vaddr, p;
vaddr = (unsigned long)page_address(page) + offset;
vaddr = (unsigned long)phys_to_virt(paddr);
for (p = vaddr & PAGE_MASK; p < vaddr + len; p += PAGE_SIZE)
flush_page_for_dma(p);
}
@@ -231,19 +233,19 @@ static dma_addr_t __sbus_iommu_map_page(struct device *dev, struct page *page,
return busa0 + off;
}
static dma_addr_t sbus_iommu_map_page_gflush(struct device *dev,
struct page *page, unsigned long offset, size_t len,
enum dma_data_direction dir, unsigned long attrs)
static dma_addr_t sbus_iommu_map_phys_gflush(struct device *dev,
phys_addr_t phys, size_t len, enum dma_data_direction dir,
unsigned long attrs)
{
flush_page_for_dma(0);
return __sbus_iommu_map_page(dev, page, offset, len, false);
return __sbus_iommu_map_phys(dev, phys, len, false, attrs);
}
static dma_addr_t sbus_iommu_map_page_pflush(struct device *dev,
struct page *page, unsigned long offset, size_t len,
enum dma_data_direction dir, unsigned long attrs)
static dma_addr_t sbus_iommu_map_phys_pflush(struct device *dev,
phys_addr_t phys, size_t len, enum dma_data_direction dir,
unsigned long attrs)
{
return __sbus_iommu_map_page(dev, page, offset, len, true);
return __sbus_iommu_map_phys(dev, phys, len, true, attrs);
}
static int __sbus_iommu_map_sg(struct device *dev, struct scatterlist *sgl,
@@ -254,8 +256,8 @@ static int __sbus_iommu_map_sg(struct device *dev, struct scatterlist *sgl,
int j;
for_each_sg(sgl, sg, nents, j) {
sg->dma_address =__sbus_iommu_map_page(dev, sg_page(sg),
sg->offset, sg->length, per_page_flush);
sg->dma_address = __sbus_iommu_map_phys(dev, sg_phys(sg),
sg->length, per_page_flush, attrs);
if (sg->dma_address == DMA_MAPPING_ERROR)
return -EIO;
sg->dma_length = sg->length;
@@ -277,7 +279,7 @@ static int sbus_iommu_map_sg_pflush(struct device *dev, struct scatterlist *sgl,
return __sbus_iommu_map_sg(dev, sgl, nents, dir, attrs, true);
}
static void sbus_iommu_unmap_page(struct device *dev, dma_addr_t dma_addr,
static void sbus_iommu_unmap_phys(struct device *dev, dma_addr_t dma_addr,
size_t len, enum dma_data_direction dir, unsigned long attrs)
{
struct iommu_struct *iommu = dev->archdata.iommu;
@@ -303,7 +305,7 @@ static void sbus_iommu_unmap_sg(struct device *dev, struct scatterlist *sgl,
int i;
for_each_sg(sgl, sg, nents, i) {
sbus_iommu_unmap_page(dev, sg->dma_address, sg->length, dir,
sbus_iommu_unmap_phys(dev, sg->dma_address, sg->length, dir,
attrs);
sg->dma_address = 0x21212121;
}
@@ -426,8 +428,8 @@ static const struct dma_map_ops sbus_iommu_dma_gflush_ops = {
.alloc = sbus_iommu_alloc,
.free = sbus_iommu_free,
#endif
.map_page = sbus_iommu_map_page_gflush,
.unmap_page = sbus_iommu_unmap_page,
.map_phys = sbus_iommu_map_phys_gflush,
.unmap_phys = sbus_iommu_unmap_phys,
.map_sg = sbus_iommu_map_sg_gflush,
.unmap_sg = sbus_iommu_unmap_sg,
};
@@ -437,8 +439,8 @@ static const struct dma_map_ops sbus_iommu_dma_pflush_ops = {
.alloc = sbus_iommu_alloc,
.free = sbus_iommu_free,
#endif
.map_page = sbus_iommu_map_page_pflush,
.unmap_page = sbus_iommu_unmap_page,
.map_phys = sbus_iommu_map_phys_pflush,
.unmap_phys = sbus_iommu_unmap_phys,
.map_sg = sbus_iommu_map_sg_pflush,
.unmap_sg = sbus_iommu_unmap_sg,
};

View File

@@ -2,6 +2,8 @@
#ifndef _ASM_X86_CPUMASK_H
#define _ASM_X86_CPUMASK_H
#ifndef __ASSEMBLER__
#include <linux/compiler.h>
#include <linux/cpumask.h>
extern void setup_cpu_local_masks(void);

View File

@@ -222,13 +222,14 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem,
}
/* Map a single area into the IOMMU */
static dma_addr_t gart_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
static dma_addr_t gart_map_phys(struct device *dev, phys_addr_t paddr,
size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
unsigned long bus;
phys_addr_t paddr = page_to_phys(page) + offset;
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
if (!need_iommu(dev, paddr, size))
return paddr;
@@ -242,7 +243,7 @@ static dma_addr_t gart_map_page(struct device *dev, struct page *page,
/*
* Free a DMA mapping.
*/
static void gart_unmap_page(struct device *dev, dma_addr_t dma_addr,
static void gart_unmap_phys(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
@@ -282,7 +283,7 @@ static void gart_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
for_each_sg(sg, s, nents, i) {
if (!s->dma_length || !s->length)
break;
gart_unmap_page(dev, s->dma_address, s->dma_length, dir, 0);
gart_unmap_phys(dev, s->dma_address, s->dma_length, dir, 0);
}
}
@@ -487,7 +488,7 @@ static void
gart_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_addr, unsigned long attrs)
{
gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, 0);
gart_unmap_phys(dev, dma_addr, size, DMA_BIDIRECTIONAL, 0);
dma_direct_free(dev, size, vaddr, dma_addr, attrs);
}
@@ -672,8 +673,8 @@ static __init int init_amd_gatt(struct agp_kern_info *info)
static const struct dma_map_ops gart_dma_ops = {
.map_sg = gart_map_sg,
.unmap_sg = gart_unmap_sg,
.map_page = gart_map_page,
.unmap_page = gart_unmap_page,
.map_phys = gart_map_phys,
.unmap_phys = gart_unmap_phys,
.alloc = gart_alloc_coherent,
.free = gart_free_coherent,
.mmap = dma_common_mmap,

View File

@@ -160,7 +160,7 @@ obj-$(CONFIG_RPMSG) += rpmsg/
obj-$(CONFIG_SOUNDWIRE) += soundwire/
# Virtualization drivers
obj-$(CONFIG_VIRT_DRIVERS) += virt/
obj-y += virt/
obj-$(CONFIG_HYPERV) += hv/
obj-$(CONFIG_PM_DEVFREQ) += devfreq/

View File

@@ -19,6 +19,7 @@ use kernel::{
cred::Credential,
error::Error,
fs::file::{self, File},
id_pool::IdPool,
list::{List, ListArc, ListArcField, ListLinks},
mm,
prelude::*,
@@ -394,6 +395,8 @@ kernel::list::impl_list_item! {
struct ProcessNodeRefs {
/// Used to look up nodes using the 32-bit id that this process knows it by.
by_handle: RBTree<u32, ListArc<NodeRefInfo, { NodeRefInfo::LIST_PROC }>>,
/// Used to quickly find unused ids in `by_handle`.
handle_is_present: IdPool,
/// Used to look up nodes without knowing their local 32-bit id. The usize is the address of
/// the underlying `Node` struct as returned by `Node::global_id`.
by_node: RBTree<usize, u32>,
@@ -408,6 +411,7 @@ impl ProcessNodeRefs {
fn new() -> Self {
Self {
by_handle: RBTree::new(),
handle_is_present: IdPool::new(),
by_node: RBTree::new(),
freeze_listeners: RBTree::new(),
}
@@ -802,7 +806,7 @@ impl Process {
pub(crate) fn insert_or_update_handle(
self: ArcBorrow<'_, Process>,
node_ref: NodeRef,
is_mananger: bool,
is_manager: bool,
) -> Result<u32> {
{
let mut refs = self.node_refs.lock();
@@ -821,7 +825,33 @@ impl Process {
let reserve2 = RBTreeNodeReservation::new(GFP_KERNEL)?;
let info = UniqueArc::new_uninit(GFP_KERNEL)?;
let mut refs = self.node_refs.lock();
let mut refs_lock = self.node_refs.lock();
let mut refs = &mut *refs_lock;
let (unused_id, by_handle_slot) = loop {
// ID 0 may only be used by the manager.
let start = if is_manager { 0 } else { 1 };
if let Some(res) = refs.handle_is_present.find_unused_id(start) {
match refs.by_handle.entry(res.as_u32()) {
rbtree::Entry::Vacant(entry) => break (res, entry),
rbtree::Entry::Occupied(_) => {
pr_err!("Detected mismatch between handle_is_present and by_handle");
res.acquire();
kernel::warn_on!(true);
return Err(EINVAL);
}
}
}
let grow_request = refs.handle_is_present.grow_request().ok_or(ENOMEM)?;
drop(refs_lock);
let resizer = grow_request.realloc(GFP_KERNEL)?;
refs_lock = self.node_refs.lock();
refs = &mut *refs_lock;
refs.handle_is_present.grow(resizer);
};
let handle = unused_id.as_u32();
// Do a lookup again as node may have been inserted before the lock was reacquired.
if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
@@ -831,20 +861,9 @@ impl Process {
return Ok(handle);
}
// Find id.
let mut target: u32 = if is_mananger { 0 } else { 1 };
for handle in refs.by_handle.keys() {
if *handle > target {
break;
}
if *handle == target {
target = target.checked_add(1).ok_or(ENOMEM)?;
}
}
let gid = node_ref.node.global_id();
let (info_proc, info_node) = {
let info_init = NodeRefInfo::new(node_ref, target, self.into());
let info_init = NodeRefInfo::new(node_ref, handle, self.into());
match info.pin_init_with(info_init) {
Ok(info) => ListArc::pair_from_pin_unique(info),
// error is infallible
@@ -865,9 +884,10 @@ impl Process {
// `info_node` into the right node's `refs` list.
unsafe { info_proc.node_ref2().node.insert_node_info(info_node) };
refs.by_node.insert(reserve1.into_node(gid, target));
refs.by_handle.insert(reserve2.into_node(target, info_proc));
Ok(target)
refs.by_node.insert(reserve1.into_node(gid, handle));
by_handle_slot.insert(info_proc, reserve2);
unused_id.acquire();
Ok(handle)
}
pub(crate) fn get_transaction_node(&self, handle: u32) -> BinderResult<NodeRef> {
@@ -932,6 +952,16 @@ impl Process {
let id = info.node_ref().node.global_id();
refs.by_handle.remove(&handle);
refs.by_node.remove(&id);
refs.handle_is_present.release_id(handle as usize);
if let Some(shrink) = refs.handle_is_present.shrink_request() {
drop(refs);
// This intentionally ignores allocation failures.
if let Ok(new_bitmap) = shrink.realloc(GFP_KERNEL) {
refs = self.node_refs.lock();
refs.handle_is_present.shrink(new_bitmap);
}
}
}
} else {
// All refs are cleared in process exit, so this warning is expected in that case.

View File

@@ -334,6 +334,19 @@ static struct device *next_device(struct klist_iter *i)
return dev;
}
static struct device *prev_device(struct klist_iter *i)
{
struct klist_node *n = klist_prev(i);
struct device *dev = NULL;
struct device_private *dev_prv;
if (n) {
dev_prv = to_device_private_bus(n);
dev = dev_prv->device;
}
return dev;
}
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
@@ -414,6 +427,31 @@ struct device *bus_find_device(const struct bus_type *bus,
}
EXPORT_SYMBOL_GPL(bus_find_device);
struct device *bus_find_device_reverse(const struct bus_type *bus,
struct device *start, const void *data,
device_match_t match)
{
struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
struct device *dev;
if (!sp)
return NULL;
klist_iter_init_node(&sp->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = prev_device(&i))) {
if (match(dev, data)) {
get_device(dev);
break;
}
}
klist_iter_exit(&i);
subsys_put(sp);
return dev;
}
EXPORT_SYMBOL_GPL(bus_find_device_reverse);
static struct device_driver *next_driver(struct klist_iter *i)
{
struct klist_node *n = klist_next(i);

View File

@@ -3,6 +3,7 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>

View File

@@ -117,9 +117,6 @@ struct at91_clk_pms {
unsigned int parent;
};
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
#define ndck(a, s) (a[s - 1].id + 1)
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)

View File

@@ -7,6 +7,7 @@
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -171,8 +172,7 @@ static u8 cpg_div6_clock_get_parent(struct clk_hw *hw)
if (clock->src_mask == 0)
return 0;
hw_index = (readl(clock->reg) & clock->src_mask) >>
__ffs(clock->src_mask);
hw_index = field_get(clock->src_mask, readl(clock->reg));
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
if (clock->parents[i] == hw_index)
return i;
@@ -191,7 +191,7 @@ static int cpg_div6_clock_set_parent(struct clk_hw *hw, u8 index)
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
src = clock->parents[index] << __ffs(clock->src_mask);
src = field_prep(clock->src_mask, clock->parents[index]);
writel((readl(clock->reg) & ~clock->src_mask) | src, clock->reg);
return 0;
}

View File

@@ -54,10 +54,8 @@ static unsigned long cpg_pll_clk_recalc_rate(struct clk_hw *hw,
{
struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
unsigned int mult;
u32 val;
val = readl(pll_clk->pllcr_reg) & CPG_PLLnCR_STC_MASK;
mult = (val >> __ffs(CPG_PLLnCR_STC_MASK)) + 1;
mult = FIELD_GET(CPG_PLLnCR_STC_MASK, readl(pll_clk->pllcr_reg)) + 1;
return parent_rate * mult * pll_clk->fixed_mult;
}
@@ -94,7 +92,7 @@ static int cpg_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
val = readl(pll_clk->pllcr_reg);
val &= ~CPG_PLLnCR_STC_MASK;
val |= (mult - 1) << __ffs(CPG_PLLnCR_STC_MASK);
val |= FIELD_PREP(CPG_PLLnCR_STC_MASK, mult - 1);
writel(val, pll_clk->pllcr_reg);
for (i = 1000; i; i--) {
@@ -176,11 +174,7 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
u32 val;
val = readl(zclk->reg) & zclk->mask;
mult = 32 - (val >> __ffs(zclk->mask));
unsigned int mult = 32 - field_get(zclk->mask, readl(zclk->reg));
return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult,
32 * zclk->fixed_div);
@@ -231,7 +225,8 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
cpg_reg_modify(zclk->reg, zclk->mask, (32 - mult) << __ffs(zclk->mask));
cpg_reg_modify(zclk->reg, zclk->mask,
field_prep(zclk->mask, 32 - mult));
/*
* Set KICK bit in FRQCRB to update hardware setting and wait for

View File

@@ -279,11 +279,7 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct cpg_z_clk *zclk = to_z_clk(hw);
unsigned int mult;
u32 val;
val = readl(zclk->reg) & zclk->mask;
mult = 32 - (val >> __ffs(zclk->mask));
unsigned int mult = 32 - field_get(zclk->mask, readl(zclk->reg));
return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * mult,
32 * zclk->fixed_div);
@@ -334,7 +330,8 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
return -EBUSY;
cpg_reg_modify(zclk->reg, zclk->mask, (32 - mult) << __ffs(zclk->mask));
cpg_reg_modify(zclk->reg, zclk->mask,
field_prep(zclk->mask, 32 - mult));
/*
* Set KICK bit in FRQCRB to update hardware setting and wait for

View File

@@ -39,6 +39,7 @@ config CRYPTO_DEV_SP_PSP
bool "Platform Security Processor (PSP) device"
default y
depends on CRYPTO_DEV_CCP_DD && X86_64 && AMD_IOMMU
select PCI_TSM if PCI
help
Provide support for the AMD Platform Security Processor (PSP).
The PSP is a dedicated processor that provides support for key

View File

@@ -16,6 +16,10 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \
hsti.o \
sfs.o
ifeq ($(CONFIG_PCI_TSM),y)
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += sev-dev-tsm.o sev-dev-tio.o
endif
obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
ccp-crypto-objs := ccp-crypto-main.o \
ccp-crypto-aes.o \

View File

@@ -0,0 +1,864 @@
// SPDX-License-Identifier: GPL-2.0-only
// Interface to PSP for CCP/SEV-TIO/SNP-VM
#include <linux/pci.h>
#include <linux/tsm.h>
#include <linux/psp.h>
#include <linux/vmalloc.h>
#include <linux/bitfield.h>
#include <linux/pci-doe.h>
#include <asm/sev-common.h>
#include <asm/sev.h>
#include <asm/page.h>
#include "sev-dev.h"
#include "sev-dev-tio.h"
#define to_tio_status(dev_data) \
(container_of((dev_data), struct tio_dsm, data)->sev->tio_status)
#define SLA_PAGE_TYPE_DATA 0
#define SLA_PAGE_TYPE_SCATTER 1
#define SLA_PAGE_SIZE_4K 0
#define SLA_PAGE_SIZE_2M 1
#define SLA_SZ(s) ((s).page_size == SLA_PAGE_SIZE_2M ? SZ_2M : SZ_4K)
#define SLA_SCATTER_LEN(s) (SLA_SZ(s) / sizeof(struct sla_addr_t))
#define SLA_EOL ((struct sla_addr_t) { .pfn = ((1UL << 40) - 1) })
#define SLA_NULL ((struct sla_addr_t) { 0 })
#define IS_SLA_NULL(s) ((s).sla == SLA_NULL.sla)
#define IS_SLA_EOL(s) ((s).sla == SLA_EOL.sla)
static phys_addr_t sla_to_pa(struct sla_addr_t sla)
{
u64 pfn = sla.pfn;
u64 pa = pfn << PAGE_SHIFT;
return pa;
}
static void *sla_to_va(struct sla_addr_t sla)
{
void *va = __va(__sme_clr(sla_to_pa(sla)));
return va;
}
#define sla_to_pfn(sla) (__pa(sla_to_va(sla)) >> PAGE_SHIFT)
#define sla_to_page(sla) virt_to_page(sla_to_va(sla))
static struct sla_addr_t make_sla(struct page *pg, bool stp)
{
u64 pa = __sme_set(page_to_phys(pg));
struct sla_addr_t ret = {
.pfn = pa >> PAGE_SHIFT,
.page_size = SLA_PAGE_SIZE_4K, /* Do not do SLA_PAGE_SIZE_2M ATM */
.page_type = stp ? SLA_PAGE_TYPE_SCATTER : SLA_PAGE_TYPE_DATA
};
return ret;
}
/* the BUFFER Structure */
#define SLA_BUFFER_FLAG_ENCRYPTION BIT(0)
/*
* struct sla_buffer_hdr - Scatter list address buffer header
*
* @capacity_sz: Total capacity of the buffer in bytes
* @payload_sz: Size of buffer payload in bytes, must be multiple of 32B
* @flags: Buffer flags (SLA_BUFFER_FLAG_ENCRYPTION: buffer is encrypted)
* @iv: Initialization vector used for encryption
* @authtag: Authentication tag for encrypted buffer
*/
struct sla_buffer_hdr {
u32 capacity_sz;
u32 payload_sz; /* The size of BUFFER_PAYLOAD in bytes. Must be multiple of 32B */
u32 flags;
u8 reserved1[4];
u8 iv[16]; /* IV used for the encryption of this buffer */
u8 authtag[16]; /* Authentication tag for this buffer */
u8 reserved2[16];
} __packed;
enum spdm_data_type_t {
DOBJ_DATA_TYPE_SPDM = 0x1,
DOBJ_DATA_TYPE_SECURE_SPDM = 0x2,
};
struct spdm_dobj_hdr_req {
struct spdm_dobj_hdr hdr; /* hdr.id == SPDM_DOBJ_ID_REQ */
u8 data_type; /* spdm_data_type_t */
u8 reserved2[5];
} __packed;
struct spdm_dobj_hdr_resp {
struct spdm_dobj_hdr hdr; /* hdr.id == SPDM_DOBJ_ID_RESP */
u8 data_type; /* spdm_data_type_t */
u8 reserved2[5];
} __packed;
/* Defined in sev-dev-tio.h so sev-dev-tsm.c can read types of blobs */
struct spdm_dobj_hdr_cert;
struct spdm_dobj_hdr_meas;
struct spdm_dobj_hdr_report;
/* Used in all SPDM-aware TIO commands */
struct spdm_ctrl {
struct sla_addr_t req;
struct sla_addr_t resp;
struct sla_addr_t scratch;
struct sla_addr_t output;
} __packed;
static size_t sla_dobj_id_to_size(u8 id)
{
size_t n;
BUILD_BUG_ON(sizeof(struct spdm_dobj_hdr_resp) != 0x10);
switch (id) {
case SPDM_DOBJ_ID_REQ:
n = sizeof(struct spdm_dobj_hdr_req);
break;
case SPDM_DOBJ_ID_RESP:
n = sizeof(struct spdm_dobj_hdr_resp);
break;
default:
WARN_ON(1);
n = 0;
break;
}
return n;
}
#define SPDM_DOBJ_HDR_SIZE(hdr) sla_dobj_id_to_size((hdr)->id)
#define SPDM_DOBJ_DATA(hdr) ((u8 *)(hdr) + SPDM_DOBJ_HDR_SIZE(hdr))
#define SPDM_DOBJ_LEN(hdr) ((hdr)->length - SPDM_DOBJ_HDR_SIZE(hdr))
#define sla_to_dobj_resp_hdr(buf) ((struct spdm_dobj_hdr_resp *) \
sla_to_dobj_hdr_check((buf), SPDM_DOBJ_ID_RESP))
#define sla_to_dobj_req_hdr(buf) ((struct spdm_dobj_hdr_req *) \
sla_to_dobj_hdr_check((buf), SPDM_DOBJ_ID_REQ))
static struct spdm_dobj_hdr *sla_to_dobj_hdr(struct sla_buffer_hdr *buf)
{
if (!buf)
return NULL;
return (struct spdm_dobj_hdr *) &buf[1];
}
static struct spdm_dobj_hdr *sla_to_dobj_hdr_check(struct sla_buffer_hdr *buf, u32 check_dobjid)
{
struct spdm_dobj_hdr *hdr = sla_to_dobj_hdr(buf);
if (WARN_ON_ONCE(!hdr))
return NULL;
if (hdr->id != check_dobjid) {
pr_err("! ERROR: expected %d, found %d\n", check_dobjid, hdr->id);
return NULL;
}
return hdr;
}
static void *sla_to_data(struct sla_buffer_hdr *buf, u32 dobjid)
{
struct spdm_dobj_hdr *hdr = sla_to_dobj_hdr(buf);
if (WARN_ON_ONCE(dobjid != SPDM_DOBJ_ID_REQ && dobjid != SPDM_DOBJ_ID_RESP))
return NULL;
if (!hdr)
return NULL;
return (u8 *) hdr + sla_dobj_id_to_size(dobjid);
}
/*
* struct sev_data_tio_status - SEV_CMD_TIO_STATUS command
*
* @length: Length of this command buffer in bytes
* @status_paddr: System physical address of the TIO_STATUS structure
*/
struct sev_data_tio_status {
u32 length;
u8 reserved[4];
u64 status_paddr;
} __packed;
/* TIO_INIT */
struct sev_data_tio_init {
u32 length;
u8 reserved[12];
} __packed;
/*
* struct sev_data_tio_dev_create - TIO_DEV_CREATE command
*
* @length: Length in bytes of this command buffer
* @dev_ctx_sla: Scatter list address pointing to a buffer to be used as a device context buffer
* @device_id: PCIe Routing Identifier of the device to connect to
* @root_port_id: PCIe Routing Identifier of the root port of the device
* @segment_id: PCIe Segment Identifier of the device to connect to
*/
struct sev_data_tio_dev_create {
u32 length;
u8 reserved1[4];
struct sla_addr_t dev_ctx_sla;
u16 device_id;
u16 root_port_id;
u8 segment_id;
u8 reserved2[11];
} __packed;
/*
* struct sev_data_tio_dev_connect - TIO_DEV_CONNECT command
*
* @length: Length in bytes of this command buffer
* @spdm_ctrl: SPDM control structure defined in Section 5.1
* @dev_ctx_sla: Scatter list address of the device context buffer
* @tc_mask: Bitmask of the traffic classes to initialize for SEV-TIO usage.
* Setting the kth bit of the TC_MASK to 1 indicates that the traffic
* class k will be initialized
* @cert_slot: Slot number of the certificate requested for constructing the SPDM session
* @ide_stream_id: IDE stream IDs to be associated with this device.
* Valid only if corresponding bit in TC_MASK is set
*/
struct sev_data_tio_dev_connect {
u32 length;
u8 reserved1[4];
struct spdm_ctrl spdm_ctrl;
u8 reserved2[8];
struct sla_addr_t dev_ctx_sla;
u8 tc_mask;
u8 cert_slot;
u8 reserved3[6];
u8 ide_stream_id[8];
u8 reserved4[8];
} __packed;
/*
* struct sev_data_tio_dev_disconnect - TIO_DEV_DISCONNECT command
*
* @length: Length in bytes of this command buffer
* @flags: Command flags (TIO_DEV_DISCONNECT_FLAG_FORCE: force disconnect)
* @spdm_ctrl: SPDM control structure defined in Section 5.1
* @dev_ctx_sla: Scatter list address of the device context buffer
*/
#define TIO_DEV_DISCONNECT_FLAG_FORCE BIT(0)
struct sev_data_tio_dev_disconnect {
u32 length;
u32 flags;
struct spdm_ctrl spdm_ctrl;
struct sla_addr_t dev_ctx_sla;
} __packed;
/*
* struct sev_data_tio_dev_meas - TIO_DEV_MEASUREMENTS command
*
* @length: Length in bytes of this command buffer
* @flags: Command flags (TIO_DEV_MEAS_FLAG_RAW_BITSTREAM: request raw measurements)
* @spdm_ctrl: SPDM control structure defined in Section 5.1
* @dev_ctx_sla: Scatter list address of the device context buffer
* @meas_nonce: Nonce for measurement freshness verification
*/
#define TIO_DEV_MEAS_FLAG_RAW_BITSTREAM BIT(0)
struct sev_data_tio_dev_meas {
u32 length;
u32 flags;
struct spdm_ctrl spdm_ctrl;
struct sla_addr_t dev_ctx_sla;
u8 meas_nonce[32];
} __packed;
/*
* struct sev_data_tio_dev_certs - TIO_DEV_CERTIFICATES command
*
* @length: Length in bytes of this command buffer
* @spdm_ctrl: SPDM control structure defined in Section 5.1
* @dev_ctx_sla: Scatter list address of the device context buffer
*/
struct sev_data_tio_dev_certs {
u32 length;
u8 reserved[4];
struct spdm_ctrl spdm_ctrl;
struct sla_addr_t dev_ctx_sla;
} __packed;
/*
* struct sev_data_tio_dev_reclaim - TIO_DEV_RECLAIM command
*
* @length: Length in bytes of this command buffer
* @dev_ctx_sla: Scatter list address of the device context buffer
*
* This command reclaims resources associated with a device context.
*/
struct sev_data_tio_dev_reclaim {
u32 length;
u8 reserved[4];
struct sla_addr_t dev_ctx_sla;
} __packed;
static struct sla_buffer_hdr *sla_buffer_map(struct sla_addr_t sla)
{
struct sla_buffer_hdr *buf;
BUILD_BUG_ON(sizeof(struct sla_buffer_hdr) != 0x40);
if (IS_SLA_NULL(sla))
return NULL;
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
struct sla_addr_t *scatter = sla_to_va(sla);
unsigned int i, npages = 0;
for (i = 0; i < SLA_SCATTER_LEN(sla); ++i) {
if (WARN_ON_ONCE(SLA_SZ(scatter[i]) > SZ_4K))
return NULL;
if (WARN_ON_ONCE(scatter[i].page_type == SLA_PAGE_TYPE_SCATTER))
return NULL;
if (IS_SLA_EOL(scatter[i])) {
npages = i;
break;
}
}
if (WARN_ON_ONCE(!npages))
return NULL;
struct page **pp = kmalloc_array(npages, sizeof(pp[0]), GFP_KERNEL);
if (!pp)
return NULL;
for (i = 0; i < npages; ++i)
pp[i] = sla_to_page(scatter[i]);
buf = vm_map_ram(pp, npages, 0);
kfree(pp);
} else {
struct page *pg = sla_to_page(sla);
buf = vm_map_ram(&pg, 1, 0);
}
return buf;
}
static void sla_buffer_unmap(struct sla_addr_t sla, struct sla_buffer_hdr *buf)
{
if (!buf)
return;
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
struct sla_addr_t *scatter = sla_to_va(sla);
unsigned int i, npages = 0;
for (i = 0; i < SLA_SCATTER_LEN(sla); ++i) {
if (IS_SLA_EOL(scatter[i])) {
npages = i;
break;
}
}
if (!npages)
return;
vm_unmap_ram(buf, npages);
} else {
vm_unmap_ram(buf, 1);
}
}
static void dobj_response_init(struct sla_buffer_hdr *buf)
{
struct spdm_dobj_hdr *dobj = sla_to_dobj_hdr(buf);
dobj->id = SPDM_DOBJ_ID_RESP;
dobj->version.major = 0x1;
dobj->version.minor = 0;
dobj->length = 0;
buf->payload_sz = sla_dobj_id_to_size(dobj->id) + dobj->length;
}
static void sla_free(struct sla_addr_t sla, size_t len, bool firmware_state)
{
unsigned int npages = PAGE_ALIGN(len) >> PAGE_SHIFT;
struct sla_addr_t *scatter = NULL;
int ret = 0, i;
if (IS_SLA_NULL(sla))
return;
if (firmware_state) {
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
scatter = sla_to_va(sla);
for (i = 0; i < npages; ++i) {
if (IS_SLA_EOL(scatter[i]))
break;
ret = snp_reclaim_pages(sla_to_pa(scatter[i]), 1, false);
if (ret)
break;
}
} else {
ret = snp_reclaim_pages(sla_to_pa(sla), 1, false);
}
}
if (WARN_ON(ret))
return;
if (scatter) {
for (i = 0; i < npages; ++i) {
if (IS_SLA_EOL(scatter[i]))
break;
free_page((unsigned long)sla_to_va(scatter[i]));
}
}
free_page((unsigned long)sla_to_va(sla));
}
static struct sla_addr_t sla_alloc(size_t len, bool firmware_state)
{
unsigned long i, npages = PAGE_ALIGN(len) >> PAGE_SHIFT;
struct sla_addr_t *scatter = NULL;
struct sla_addr_t ret = SLA_NULL;
struct sla_buffer_hdr *buf;
struct page *pg;
if (npages == 0)
return ret;
if (WARN_ON_ONCE(npages > ((PAGE_SIZE / sizeof(struct sla_addr_t)) + 1)))
return ret;
BUILD_BUG_ON(PAGE_SIZE < SZ_4K);
if (npages > 1) {
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!pg)
return SLA_NULL;
ret = make_sla(pg, true);
scatter = page_to_virt(pg);
for (i = 0; i < npages; ++i) {
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!pg)
goto no_reclaim_exit;
scatter[i] = make_sla(pg, false);
}
scatter[i] = SLA_EOL;
} else {
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!pg)
return SLA_NULL;
ret = make_sla(pg, false);
}
buf = sla_buffer_map(ret);
if (!buf)
goto no_reclaim_exit;
buf->capacity_sz = (npages << PAGE_SHIFT);
sla_buffer_unmap(ret, buf);
if (firmware_state) {
if (scatter) {
for (i = 0; i < npages; ++i) {
if (rmp_make_private(sla_to_pfn(scatter[i]), 0,
PG_LEVEL_4K, 0, true))
goto free_exit;
}
} else {
if (rmp_make_private(sla_to_pfn(ret), 0, PG_LEVEL_4K, 0, true))
goto no_reclaim_exit;
}
}
return ret;
no_reclaim_exit:
firmware_state = false;
free_exit:
sla_free(ret, len, firmware_state);
return SLA_NULL;
}
/* Expands a buffer, only firmware owned buffers allowed for now */
static int sla_expand(struct sla_addr_t *sla, size_t *len)
{
struct sla_buffer_hdr *oldbuf = sla_buffer_map(*sla), *newbuf;
struct sla_addr_t oldsla = *sla, newsla;
size_t oldlen = *len, newlen;
if (!oldbuf)
return -EFAULT;
newlen = oldbuf->capacity_sz;
if (oldbuf->capacity_sz == oldlen) {
/* This buffer does not require expansion, must be another buffer */
sla_buffer_unmap(oldsla, oldbuf);
return 1;
}
pr_notice("Expanding BUFFER from %ld to %ld bytes\n", oldlen, newlen);
newsla = sla_alloc(newlen, true);
if (IS_SLA_NULL(newsla))
return -ENOMEM;
newbuf = sla_buffer_map(newsla);
if (!newbuf) {
sla_free(newsla, newlen, true);
return -EFAULT;
}
memcpy(newbuf, oldbuf, oldlen);
sla_buffer_unmap(newsla, newbuf);
sla_free(oldsla, oldlen, true);
*sla = newsla;
*len = newlen;
return 0;
}
static int sev_tio_do_cmd(int cmd, void *data, size_t data_len, int *psp_ret,
struct tsm_dsm_tio *dev_data)
{
int rc;
*psp_ret = 0;
rc = sev_do_cmd(cmd, data, psp_ret);
if (WARN_ON(!rc && *psp_ret == SEV_RET_SPDM_REQUEST))
return -EIO;
if (rc == 0 && *psp_ret == SEV_RET_EXPAND_BUFFER_LENGTH_REQUEST) {
int rc1, rc2;
rc1 = sla_expand(&dev_data->output, &dev_data->output_len);
if (rc1 < 0)
return rc1;
rc2 = sla_expand(&dev_data->scratch, &dev_data->scratch_len);
if (rc2 < 0)
return rc2;
if (!rc1 && !rc2)
/* Neither buffer requires expansion, this is wrong */
return -EFAULT;
*psp_ret = 0;
rc = sev_do_cmd(cmd, data, psp_ret);
}
if ((rc == 0 || rc == -EIO) && *psp_ret == SEV_RET_SPDM_REQUEST) {
struct spdm_dobj_hdr_resp *resp_hdr;
struct spdm_dobj_hdr_req *req_hdr;
struct sev_tio_status *tio_status = to_tio_status(dev_data);
size_t resp_len = tio_status->spdm_req_size_max -
(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) + sizeof(struct sla_buffer_hdr));
if (!dev_data->cmd) {
if (WARN_ON_ONCE(!data_len || (data_len != *(u32 *) data)))
return -EINVAL;
if (WARN_ON(data_len > sizeof(dev_data->cmd_data)))
return -EFAULT;
memcpy(dev_data->cmd_data, data, data_len);
memset(&dev_data->cmd_data[data_len], 0xFF,
sizeof(dev_data->cmd_data) - data_len);
dev_data->cmd = cmd;
}
req_hdr = sla_to_dobj_req_hdr(dev_data->reqbuf);
resp_hdr = sla_to_dobj_resp_hdr(dev_data->respbuf);
switch (req_hdr->data_type) {
case DOBJ_DATA_TYPE_SPDM:
rc = PCI_DOE_FEATURE_CMA;
break;
case DOBJ_DATA_TYPE_SECURE_SPDM:
rc = PCI_DOE_FEATURE_SSESSION;
break;
default:
return -EINVAL;
}
resp_hdr->data_type = req_hdr->data_type;
dev_data->spdm.req_len = req_hdr->hdr.length -
sla_dobj_id_to_size(SPDM_DOBJ_ID_REQ);
dev_data->spdm.rsp_len = resp_len;
} else if (dev_data && dev_data->cmd) {
/* For either error or success just stop the bouncing */
memset(dev_data->cmd_data, 0, sizeof(dev_data->cmd_data));
dev_data->cmd = 0;
}
return rc;
}
int sev_tio_continue(struct tsm_dsm_tio *dev_data)
{
struct spdm_dobj_hdr_resp *resp_hdr;
int ret;
if (!dev_data || !dev_data->cmd)
return -EINVAL;
resp_hdr = sla_to_dobj_resp_hdr(dev_data->respbuf);
resp_hdr->hdr.length = ALIGN(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) +
dev_data->spdm.rsp_len, 32);
dev_data->respbuf->payload_sz = resp_hdr->hdr.length;
ret = sev_tio_do_cmd(dev_data->cmd, dev_data->cmd_data, 0,
&dev_data->psp_ret, dev_data);
if (ret)
return ret;
if (dev_data->psp_ret != SEV_RET_SUCCESS)
return -EINVAL;
return 0;
}
static void spdm_ctrl_init(struct spdm_ctrl *ctrl, struct tsm_dsm_tio *dev_data)
{
ctrl->req = dev_data->req;
ctrl->resp = dev_data->resp;
ctrl->scratch = dev_data->scratch;
ctrl->output = dev_data->output;
}
static void spdm_ctrl_free(struct tsm_dsm_tio *dev_data)
{
struct sev_tio_status *tio_status = to_tio_status(dev_data);
size_t len = tio_status->spdm_req_size_max -
(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) +
sizeof(struct sla_buffer_hdr));
struct tsm_spdm *spdm = &dev_data->spdm;
sla_buffer_unmap(dev_data->resp, dev_data->respbuf);
sla_buffer_unmap(dev_data->req, dev_data->reqbuf);
spdm->rsp = NULL;
spdm->req = NULL;
sla_free(dev_data->req, len, true);
sla_free(dev_data->resp, len, false);
sla_free(dev_data->scratch, tio_status->spdm_scratch_size_max, true);
dev_data->req.sla = 0;
dev_data->resp.sla = 0;
dev_data->scratch.sla = 0;
dev_data->respbuf = NULL;
dev_data->reqbuf = NULL;
sla_free(dev_data->output, tio_status->spdm_out_size_max, true);
}
static int spdm_ctrl_alloc(struct tsm_dsm_tio *dev_data)
{
struct sev_tio_status *tio_status = to_tio_status(dev_data);
struct tsm_spdm *spdm = &dev_data->spdm;
int ret;
dev_data->req = sla_alloc(tio_status->spdm_req_size_max, true);
dev_data->resp = sla_alloc(tio_status->spdm_req_size_max, false);
dev_data->scratch_len = tio_status->spdm_scratch_size_max;
dev_data->scratch = sla_alloc(dev_data->scratch_len, true);
dev_data->output_len = tio_status->spdm_out_size_max;
dev_data->output = sla_alloc(dev_data->output_len, true);
if (IS_SLA_NULL(dev_data->req) || IS_SLA_NULL(dev_data->resp) ||
IS_SLA_NULL(dev_data->scratch) || IS_SLA_NULL(dev_data->dev_ctx)) {
ret = -ENOMEM;
goto free_spdm_exit;
}
dev_data->reqbuf = sla_buffer_map(dev_data->req);
dev_data->respbuf = sla_buffer_map(dev_data->resp);
if (!dev_data->reqbuf || !dev_data->respbuf) {
ret = -EFAULT;
goto free_spdm_exit;
}
spdm->req = sla_to_data(dev_data->reqbuf, SPDM_DOBJ_ID_REQ);
spdm->rsp = sla_to_data(dev_data->respbuf, SPDM_DOBJ_ID_RESP);
if (!spdm->req || !spdm->rsp) {
ret = -EFAULT;
goto free_spdm_exit;
}
dobj_response_init(dev_data->respbuf);
return 0;
free_spdm_exit:
spdm_ctrl_free(dev_data);
return ret;
}
int sev_tio_init_locked(void *tio_status_page)
{
struct sev_tio_status *tio_status = tio_status_page;
struct sev_data_tio_status data_status = {
.length = sizeof(data_status),
};
int ret, psp_ret;
data_status.status_paddr = __psp_pa(tio_status_page);
ret = __sev_do_cmd_locked(SEV_CMD_TIO_STATUS, &data_status, &psp_ret);
if (ret)
return ret;
if (tio_status->length < offsetofend(struct sev_tio_status, tdictx_size) ||
tio_status->reserved)
return -EFAULT;
if (!tio_status->tio_en && !tio_status->tio_init_done)
return -ENOENT;
if (tio_status->tio_init_done)
return -EBUSY;
struct sev_data_tio_init ti = { .length = sizeof(ti) };
ret = __sev_do_cmd_locked(SEV_CMD_TIO_INIT, &ti, &psp_ret);
if (ret)
return ret;
ret = __sev_do_cmd_locked(SEV_CMD_TIO_STATUS, &data_status, &psp_ret);
if (ret)
return ret;
return 0;
}
int sev_tio_dev_create(struct tsm_dsm_tio *dev_data, u16 device_id,
u16 root_port_id, u8 segment_id)
{
struct sev_tio_status *tio_status = to_tio_status(dev_data);
struct sev_data_tio_dev_create create = {
.length = sizeof(create),
.device_id = device_id,
.root_port_id = root_port_id,
.segment_id = segment_id,
};
void *data_pg;
int ret;
dev_data->dev_ctx = sla_alloc(tio_status->devctx_size, true);
if (IS_SLA_NULL(dev_data->dev_ctx))
return -ENOMEM;
data_pg = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT);
if (!data_pg) {
ret = -ENOMEM;
goto free_ctx_exit;
}
create.dev_ctx_sla = dev_data->dev_ctx;
ret = sev_do_cmd(SEV_CMD_TIO_DEV_CREATE, &create, &dev_data->psp_ret);
if (ret)
goto free_data_pg_exit;
dev_data->data_pg = data_pg;
return 0;
free_data_pg_exit:
snp_free_firmware_page(data_pg);
free_ctx_exit:
sla_free(create.dev_ctx_sla, tio_status->devctx_size, true);
return ret;
}
int sev_tio_dev_reclaim(struct tsm_dsm_tio *dev_data)
{
struct sev_tio_status *tio_status = to_tio_status(dev_data);
struct sev_data_tio_dev_reclaim r = {
.length = sizeof(r),
.dev_ctx_sla = dev_data->dev_ctx,
};
int ret;
if (dev_data->data_pg) {
snp_free_firmware_page(dev_data->data_pg);
dev_data->data_pg = NULL;
}
if (IS_SLA_NULL(dev_data->dev_ctx))
return 0;
ret = sev_do_cmd(SEV_CMD_TIO_DEV_RECLAIM, &r, &dev_data->psp_ret);
sla_free(dev_data->dev_ctx, tio_status->devctx_size, true);
dev_data->dev_ctx = SLA_NULL;
spdm_ctrl_free(dev_data);
return ret;
}
int sev_tio_dev_connect(struct tsm_dsm_tio *dev_data, u8 tc_mask, u8 ids[8], u8 cert_slot)
{
struct sev_data_tio_dev_connect connect = {
.length = sizeof(connect),
.tc_mask = tc_mask,
.cert_slot = cert_slot,
.dev_ctx_sla = dev_data->dev_ctx,
.ide_stream_id = {
ids[0], ids[1], ids[2], ids[3],
ids[4], ids[5], ids[6], ids[7]
},
};
int ret;
if (WARN_ON(IS_SLA_NULL(dev_data->dev_ctx)))
return -EFAULT;
if (!(tc_mask & 1))
return -EINVAL;
ret = spdm_ctrl_alloc(dev_data);
if (ret)
return ret;
spdm_ctrl_init(&connect.spdm_ctrl, dev_data);
return sev_tio_do_cmd(SEV_CMD_TIO_DEV_CONNECT, &connect, sizeof(connect),
&dev_data->psp_ret, dev_data);
}
int sev_tio_dev_disconnect(struct tsm_dsm_tio *dev_data, bool force)
{
struct sev_data_tio_dev_disconnect dc = {
.length = sizeof(dc),
.dev_ctx_sla = dev_data->dev_ctx,
.flags = force ? TIO_DEV_DISCONNECT_FLAG_FORCE : 0,
};
if (WARN_ON_ONCE(IS_SLA_NULL(dev_data->dev_ctx)))
return -EFAULT;
spdm_ctrl_init(&dc.spdm_ctrl, dev_data);
return sev_tio_do_cmd(SEV_CMD_TIO_DEV_DISCONNECT, &dc, sizeof(dc),
&dev_data->psp_ret, dev_data);
}
int sev_tio_cmd_buffer_len(int cmd)
{
switch (cmd) {
case SEV_CMD_TIO_STATUS: return sizeof(struct sev_data_tio_status);
case SEV_CMD_TIO_INIT: return sizeof(struct sev_data_tio_init);
case SEV_CMD_TIO_DEV_CREATE: return sizeof(struct sev_data_tio_dev_create);
case SEV_CMD_TIO_DEV_RECLAIM: return sizeof(struct sev_data_tio_dev_reclaim);
case SEV_CMD_TIO_DEV_CONNECT: return sizeof(struct sev_data_tio_dev_connect);
case SEV_CMD_TIO_DEV_DISCONNECT: return sizeof(struct sev_data_tio_dev_disconnect);
default: return 0;
}
}

View File

@@ -0,0 +1,123 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __PSP_SEV_TIO_H__
#define __PSP_SEV_TIO_H__
#include <linux/pci-tsm.h>
#include <linux/pci-ide.h>
#include <linux/tsm.h>
#include <uapi/linux/psp-sev.h>
struct sla_addr_t {
union {
u64 sla;
struct {
u64 page_type :1,
page_size :1,
reserved1 :10,
pfn :40,
reserved2 :12;
};
};
} __packed;
#define SEV_TIO_MAX_COMMAND_LENGTH 128
/* SPDM control structure for DOE */
struct tsm_spdm {
unsigned long req_len;
void *req;
unsigned long rsp_len;
void *rsp;
};
/* Describes TIO device */
struct tsm_dsm_tio {
u8 cert_slot;
struct sla_addr_t dev_ctx;
struct sla_addr_t req;
struct sla_addr_t resp;
struct sla_addr_t scratch;
struct sla_addr_t output;
size_t output_len;
size_t scratch_len;
struct tsm_spdm spdm;
struct sla_buffer_hdr *reqbuf; /* vmap'ed @req for DOE */
struct sla_buffer_hdr *respbuf; /* vmap'ed @resp for DOE */
int cmd;
int psp_ret;
u8 cmd_data[SEV_TIO_MAX_COMMAND_LENGTH];
void *data_pg; /* Data page for DEV_STATUS/TDI_STATUS/TDI_INFO/ASID_FENCE */
#define TIO_IDE_MAX_TC 8
struct pci_ide *ide[TIO_IDE_MAX_TC];
};
/* Describes TSM structure for PF0 pointed by pci_dev->tsm */
struct tio_dsm {
struct pci_tsm_pf0 tsm;
struct tsm_dsm_tio data;
struct sev_device *sev;
};
/* Data object IDs */
#define SPDM_DOBJ_ID_NONE 0
#define SPDM_DOBJ_ID_REQ 1
#define SPDM_DOBJ_ID_RESP 2
struct spdm_dobj_hdr {
u32 id; /* Data object type identifier */
u32 length; /* Length of the data object, INCLUDING THIS HEADER */
struct { /* Version of the data object structure */
u8 minor;
u8 major;
} version;
} __packed;
/**
* struct sev_tio_status - TIO_STATUS command's info_paddr buffer
*
* @length: Length of this structure in bytes
* @tio_en: Indicates that SNP_INIT_EX initialized the RMP for SEV-TIO
* @tio_init_done: Indicates TIO_INIT has been invoked
* @spdm_req_size_min: Minimum SPDM request buffer size in bytes
* @spdm_req_size_max: Maximum SPDM request buffer size in bytes
* @spdm_scratch_size_min: Minimum SPDM scratch buffer size in bytes
* @spdm_scratch_size_max: Maximum SPDM scratch buffer size in bytes
* @spdm_out_size_min: Minimum SPDM output buffer size in bytes
* @spdm_out_size_max: Maximum for the SPDM output buffer size in bytes
* @spdm_rsp_size_min: Minimum SPDM response buffer size in bytes
* @spdm_rsp_size_max: Maximum SPDM response buffer size in bytes
* @devctx_size: Size of a device context buffer in bytes
* @tdictx_size: Size of a TDI context buffer in bytes
* @tio_crypto_alg: TIO crypto algorithms supported
*/
struct sev_tio_status {
u32 length;
u32 tio_en :1,
tio_init_done :1,
reserved :30;
u32 spdm_req_size_min;
u32 spdm_req_size_max;
u32 spdm_scratch_size_min;
u32 spdm_scratch_size_max;
u32 spdm_out_size_min;
u32 spdm_out_size_max;
u32 spdm_rsp_size_min;
u32 spdm_rsp_size_max;
u32 devctx_size;
u32 tdictx_size;
u32 tio_crypto_alg;
u8 reserved2[12];
} __packed;
int sev_tio_init_locked(void *tio_status_page);
int sev_tio_continue(struct tsm_dsm_tio *dev_data);
int sev_tio_dev_create(struct tsm_dsm_tio *dev_data, u16 device_id, u16 root_port_id,
u8 segment_id);
int sev_tio_dev_connect(struct tsm_dsm_tio *dev_data, u8 tc_mask, u8 ids[8], u8 cert_slot);
int sev_tio_dev_disconnect(struct tsm_dsm_tio *dev_data, bool force);
int sev_tio_dev_reclaim(struct tsm_dsm_tio *dev_data);
#endif /* __PSP_SEV_TIO_H__ */

View File

@@ -0,0 +1,405 @@
// SPDX-License-Identifier: GPL-2.0-only
// Interface to CCP/SEV-TIO for generic PCIe TDISP module
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/tsm.h>
#include <linux/iommu.h>
#include <linux/pci-doe.h>
#include <linux/bitfield.h>
#include <linux/module.h>
#include <asm/sev-common.h>
#include <asm/sev.h>
#include "psp-dev.h"
#include "sev-dev.h"
#include "sev-dev-tio.h"
MODULE_IMPORT_NS("PCI_IDE");
#define TIO_DEFAULT_NR_IDE_STREAMS 1
static uint nr_ide_streams = TIO_DEFAULT_NR_IDE_STREAMS;
module_param_named(ide_nr, nr_ide_streams, uint, 0644);
MODULE_PARM_DESC(ide_nr, "Set the maximum number of IDE streams per PHB");
#define dev_to_sp(dev) ((struct sp_device *)dev_get_drvdata(dev))
#define dev_to_psp(dev) ((struct psp_device *)(dev_to_sp(dev)->psp_data))
#define dev_to_sev(dev) ((struct sev_device *)(dev_to_psp(dev)->sev_data))
#define tsm_dev_to_sev(tsmdev) dev_to_sev((tsmdev)->dev.parent)
#define pdev_to_tio_dsm(pdev) (container_of((pdev)->tsm, struct tio_dsm, tsm.base_tsm))
static int sev_tio_spdm_cmd(struct tio_dsm *dsm, int ret)
{
struct tsm_dsm_tio *dev_data = &dsm->data;
struct tsm_spdm *spdm = &dev_data->spdm;
/* Check the main command handler response before entering the loop */
if (ret == 0 && dev_data->psp_ret != SEV_RET_SUCCESS)
return -EINVAL;
if (ret <= 0)
return ret;
/* ret > 0 means "SPDM requested" */
while (ret == PCI_DOE_FEATURE_CMA || ret == PCI_DOE_FEATURE_SSESSION) {
ret = pci_doe(dsm->tsm.doe_mb, PCI_VENDOR_ID_PCI_SIG, ret,
spdm->req, spdm->req_len, spdm->rsp, spdm->rsp_len);
if (ret < 0)
break;
WARN_ON_ONCE(ret == 0); /* The response should never be empty */
spdm->rsp_len = ret;
ret = sev_tio_continue(dev_data);
}
return ret;
}
static int stream_enable(struct pci_ide *ide)
{
struct pci_dev *rp = pcie_find_root_port(ide->pdev);
int ret;
ret = pci_ide_stream_enable(rp, ide);
if (ret)
return ret;
ret = pci_ide_stream_enable(ide->pdev, ide);
if (ret)
pci_ide_stream_disable(rp, ide);
return ret;
}
static int streams_enable(struct pci_ide **ide)
{
int ret = 0;
for (int i = 0; i < TIO_IDE_MAX_TC; ++i) {
if (ide[i]) {
ret = stream_enable(ide[i]);
if (ret)
break;
}
}
return ret;
}
static void stream_disable(struct pci_ide *ide)
{
pci_ide_stream_disable(ide->pdev, ide);
pci_ide_stream_disable(pcie_find_root_port(ide->pdev), ide);
}
static void streams_disable(struct pci_ide **ide)
{
for (int i = 0; i < TIO_IDE_MAX_TC; ++i)
if (ide[i])
stream_disable(ide[i]);
}
static void stream_setup(struct pci_ide *ide)
{
struct pci_dev *rp = pcie_find_root_port(ide->pdev);
ide->partner[PCI_IDE_EP].rid_start = 0;
ide->partner[PCI_IDE_EP].rid_end = 0xffff;
ide->partner[PCI_IDE_RP].rid_start = 0;
ide->partner[PCI_IDE_RP].rid_end = 0xffff;
ide->pdev->ide_cfg = 0;
ide->pdev->ide_tee_limit = 1;
rp->ide_cfg = 1;
rp->ide_tee_limit = 0;
pci_warn(ide->pdev, "Forcing CFG/TEE for %s", pci_name(rp));
pci_ide_stream_setup(ide->pdev, ide);
pci_ide_stream_setup(rp, ide);
}
static u8 streams_setup(struct pci_ide **ide, u8 *ids)
{
bool def = false;
u8 tc_mask = 0;
int i;
for (i = 0; i < TIO_IDE_MAX_TC; ++i) {
if (!ide[i]) {
ids[i] = 0xFF;
continue;
}
tc_mask |= BIT(i);
ids[i] = ide[i]->stream_id;
if (!def) {
struct pci_ide_partner *settings;
settings = pci_ide_to_settings(ide[i]->pdev, ide[i]);
settings->default_stream = 1;
def = true;
}
stream_setup(ide[i]);
}
return tc_mask;
}
static int streams_register(struct pci_ide **ide)
{
int ret = 0, i;
for (i = 0; i < TIO_IDE_MAX_TC; ++i) {
if (ide[i]) {
ret = pci_ide_stream_register(ide[i]);
if (ret)
break;
}
}
return ret;
}
static void streams_unregister(struct pci_ide **ide)
{
for (int i = 0; i < TIO_IDE_MAX_TC; ++i)
if (ide[i])
pci_ide_stream_unregister(ide[i]);
}
static void stream_teardown(struct pci_ide *ide)
{
pci_ide_stream_teardown(ide->pdev, ide);
pci_ide_stream_teardown(pcie_find_root_port(ide->pdev), ide);
}
static void streams_teardown(struct pci_ide **ide)
{
for (int i = 0; i < TIO_IDE_MAX_TC; ++i) {
if (ide[i]) {
stream_teardown(ide[i]);
pci_ide_stream_free(ide[i]);
ide[i] = NULL;
}
}
}
static int stream_alloc(struct pci_dev *pdev, struct pci_ide **ide,
unsigned int tc)
{
struct pci_dev *rp = pcie_find_root_port(pdev);
struct pci_ide *ide1;
if (ide[tc]) {
pci_err(pdev, "Stream for class=%d already registered", tc);
return -EBUSY;
}
/* FIXME: find a better way */
if (nr_ide_streams != TIO_DEFAULT_NR_IDE_STREAMS)
pci_notice(pdev, "Enable non-default %d streams", nr_ide_streams);
pci_ide_set_nr_streams(to_pci_host_bridge(rp->bus->bridge), nr_ide_streams);
ide1 = pci_ide_stream_alloc(pdev);
if (!ide1)
return -EFAULT;
/* Blindly assign streamid=0 to TC=0, and so on */
ide1->stream_id = tc;
ide[tc] = ide1;
return 0;
}
static struct pci_tsm *tio_pf0_probe(struct pci_dev *pdev, struct sev_device *sev)
{
struct tio_dsm *dsm __free(kfree) = kzalloc(sizeof(*dsm), GFP_KERNEL);
int rc;
if (!dsm)
return NULL;
rc = pci_tsm_pf0_constructor(pdev, &dsm->tsm, sev->tsmdev);
if (rc)
return NULL;
pci_dbg(pdev, "TSM enabled\n");
dsm->sev = sev;
return &no_free_ptr(dsm)->tsm.base_tsm;
}
static struct pci_tsm *dsm_probe(struct tsm_dev *tsmdev, struct pci_dev *pdev)
{
struct sev_device *sev = tsm_dev_to_sev(tsmdev);
if (is_pci_tsm_pf0(pdev))
return tio_pf0_probe(pdev, sev);
return 0;
}
static void dsm_remove(struct pci_tsm *tsm)
{
struct pci_dev *pdev = tsm->pdev;
pci_dbg(pdev, "TSM disabled\n");
if (is_pci_tsm_pf0(pdev)) {
struct tio_dsm *dsm = container_of(tsm, struct tio_dsm, tsm.base_tsm);
pci_tsm_pf0_destructor(&dsm->tsm);
kfree(dsm);
}
}
static int dsm_create(struct tio_dsm *dsm)
{
struct pci_dev *pdev = dsm->tsm.base_tsm.pdev;
u8 segment_id = pdev->bus ? pci_domain_nr(pdev->bus) : 0;
struct pci_dev *rootport = pcie_find_root_port(pdev);
u16 device_id = pci_dev_id(pdev);
u16 root_port_id;
u32 lnkcap = 0;
if (pci_read_config_dword(rootport, pci_pcie_cap(rootport) + PCI_EXP_LNKCAP,
&lnkcap))
return -ENODEV;
root_port_id = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
return sev_tio_dev_create(&dsm->data, device_id, root_port_id, segment_id);
}
static int dsm_connect(struct pci_dev *pdev)
{
struct tio_dsm *dsm = pdev_to_tio_dsm(pdev);
struct tsm_dsm_tio *dev_data = &dsm->data;
u8 ids[TIO_IDE_MAX_TC];
u8 tc_mask;
int ret;
if (pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
PCI_DOE_FEATURE_SSESSION) != dsm->tsm.doe_mb) {
pci_err(pdev, "CMA DOE MB must support SSESSION\n");
return -EFAULT;
}
ret = stream_alloc(pdev, dev_data->ide, 0);
if (ret)
return ret;
ret = dsm_create(dsm);
if (ret)
goto ide_free_exit;
tc_mask = streams_setup(dev_data->ide, ids);
ret = sev_tio_dev_connect(dev_data, tc_mask, ids, dev_data->cert_slot);
ret = sev_tio_spdm_cmd(dsm, ret);
if (ret)
goto free_exit;
streams_enable(dev_data->ide);
ret = streams_register(dev_data->ide);
if (ret)
goto free_exit;
return 0;
free_exit:
sev_tio_dev_reclaim(dev_data);
streams_disable(dev_data->ide);
ide_free_exit:
streams_teardown(dev_data->ide);
return ret;
}
static void dsm_disconnect(struct pci_dev *pdev)
{
bool force = SYSTEM_HALT <= system_state && system_state <= SYSTEM_RESTART;
struct tio_dsm *dsm = pdev_to_tio_dsm(pdev);
struct tsm_dsm_tio *dev_data = &dsm->data;
int ret;
ret = sev_tio_dev_disconnect(dev_data, force);
ret = sev_tio_spdm_cmd(dsm, ret);
if (ret && !force) {
ret = sev_tio_dev_disconnect(dev_data, true);
sev_tio_spdm_cmd(dsm, ret);
}
sev_tio_dev_reclaim(dev_data);
streams_disable(dev_data->ide);
streams_unregister(dev_data->ide);
streams_teardown(dev_data->ide);
}
static struct pci_tsm_ops sev_tsm_ops = {
.probe = dsm_probe,
.remove = dsm_remove,
.connect = dsm_connect,
.disconnect = dsm_disconnect,
};
void sev_tsm_init_locked(struct sev_device *sev, void *tio_status_page)
{
struct sev_tio_status *t = kzalloc(sizeof(*t), GFP_KERNEL);
struct tsm_dev *tsmdev;
int ret;
WARN_ON(sev->tio_status);
if (!t)
return;
ret = sev_tio_init_locked(tio_status_page);
if (ret) {
pr_warn("SEV-TIO STATUS failed with %d\n", ret);
goto error_exit;
}
tsmdev = tsm_register(sev->dev, &sev_tsm_ops);
if (IS_ERR(tsmdev))
goto error_exit;
memcpy(t, tio_status_page, sizeof(*t));
pr_notice("SEV-TIO status: EN=%d INIT_DONE=%d rq=%d..%d rs=%d..%d "
"scr=%d..%d out=%d..%d dev=%d tdi=%d algos=%x\n",
t->tio_en, t->tio_init_done,
t->spdm_req_size_min, t->spdm_req_size_max,
t->spdm_rsp_size_min, t->spdm_rsp_size_max,
t->spdm_scratch_size_min, t->spdm_scratch_size_max,
t->spdm_out_size_min, t->spdm_out_size_max,
t->devctx_size, t->tdictx_size,
t->tio_crypto_alg);
sev->tsmdev = tsmdev;
sev->tio_status = t;
return;
error_exit:
kfree(t);
pr_err("Failed to enable SEV-TIO: ret=%d en=%d initdone=%d SEV=%d\n",
ret, t->tio_en, t->tio_init_done, boot_cpu_has(X86_FEATURE_SEV));
}
void sev_tsm_uninit(struct sev_device *sev)
{
if (sev->tsmdev)
tsm_unregister(sev->tsmdev);
sev->tsmdev = NULL;
}

View File

@@ -75,6 +75,14 @@ static bool psp_init_on_probe = true;
module_param(psp_init_on_probe, bool, 0444);
MODULE_PARM_DESC(psp_init_on_probe, " if true, the PSP will be initialized on module init. Else the PSP will be initialized on the first command requiring it");
#if IS_ENABLED(CONFIG_PCI_TSM)
static bool sev_tio_enabled = true;
module_param_named(tio, sev_tio_enabled, bool, 0444);
MODULE_PARM_DESC(tio, "Enables TIO in SNP_INIT_EX");
#else
static const bool sev_tio_enabled = false;
#endif
MODULE_FIRMWARE("amd/amd_sev_fam17h_model0xh.sbin"); /* 1st gen EPYC */
MODULE_FIRMWARE("amd/amd_sev_fam17h_model3xh.sbin"); /* 2nd gen EPYC */
MODULE_FIRMWARE("amd/amd_sev_fam19h_model0xh.sbin"); /* 3rd gen EPYC */
@@ -251,7 +259,7 @@ static int sev_cmd_buffer_len(int cmd)
case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit);
case SEV_CMD_SNP_FEATURE_INFO: return sizeof(struct sev_data_snp_feature_info);
case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load);
default: return 0;
default: return sev_tio_cmd_buffer_len(cmd);
}
return 0;
@@ -380,13 +388,7 @@ static int sev_write_init_ex_file_if_required(int cmd_id)
return sev_write_init_ex_file();
}
/*
* snp_reclaim_pages() needs __sev_do_cmd_locked(), and __sev_do_cmd_locked()
* needs snp_reclaim_pages(), so a forward declaration is needed.
*/
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret);
static int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked)
int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked)
{
int ret, err, i;
@@ -420,6 +422,7 @@ cleanup:
snp_leak_pages(__phys_to_pfn(paddr), npages - i);
return ret;
}
EXPORT_SYMBOL_GPL(snp_reclaim_pages);
static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked)
{
@@ -850,7 +853,7 @@ static int snp_reclaim_cmd_buf(int cmd, void *cmd_buf)
return 0;
}
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
{
struct cmd_buf_desc desc_list[CMD_BUF_DESC_MAX] = {0};
struct psp_device *psp = psp_master;
@@ -1392,6 +1395,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
*
*/
if (sev_version_greater_or_equal(SNP_MIN_API_MAJOR, 52)) {
bool tio_supp = !!(sev->snp_feat_info_0.ebx & SNP_SEV_TIO_SUPPORTED);
/*
* Firmware checks that the pages containing the ranges enumerated
* in the RANGES structure are either in the default page state or in the
@@ -1432,6 +1437,17 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
data.init_rmp = 1;
data.list_paddr_en = 1;
data.list_paddr = __psp_pa(snp_range_list);
data.tio_en = tio_supp && sev_tio_enabled && amd_iommu_sev_tio_supported();
/*
* When psp_init_on_probe is disabled, the userspace calling
* SEV ioctl can inadvertently shut down SNP and SEV-TIO causing
* unexpected state loss.
*/
if (data.tio_en && !psp_init_on_probe)
dev_warn(sev->dev, "SEV-TIO as incompatible with psp_init_on_probe=0\n");
cmd = SEV_CMD_SNP_INIT_EX;
} else {
cmd = SEV_CMD_SNP_INIT;
@@ -1469,7 +1485,8 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
snp_hv_fixed_pages_state_update(sev, HV_FIXED);
sev->snp_initialized = true;
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
dev_dbg(sev->dev, "SEV-SNP firmware initialized, SEV-TIO is %s\n",
data.tio_en ? "enabled" : "disabled");
dev_info(sev->dev, "SEV-SNP API:%d.%d build:%d\n", sev->api_major,
sev->api_minor, sev->build);
@@ -1477,6 +1494,23 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
atomic_notifier_chain_register(&panic_notifier_list,
&snp_panic_notifier);
if (data.tio_en) {
/*
* This executes with the sev_cmd_mutex held so down the stack
* snp_reclaim_pages(locked=false) might be needed (which is extremely
* unlikely) but will cause a deadlock.
* Instead of exporting __snp_alloc_firmware_pages(), allocate a page
* for this one call here.
*/
void *tio_status = page_address(__snp_alloc_firmware_pages(
GFP_KERNEL_ACCOUNT | __GFP_ZERO, 0, true));
if (tio_status) {
sev_tsm_init_locked(sev, tio_status);
__snp_free_firmware_pages(virt_to_page(tio_status), 0, true);
}
}
sev_es_tmr_size = SNP_TMR_SIZE;
return 0;
@@ -2756,8 +2790,20 @@ static void __sev_firmware_shutdown(struct sev_device *sev, bool panic)
static void sev_firmware_shutdown(struct sev_device *sev)
{
/*
* Calling without sev_cmd_mutex held as TSM will likely try disconnecting
* IDE and this ends up calling sev_do_cmd() which locks sev_cmd_mutex.
*/
if (sev->tio_status)
sev_tsm_uninit(sev);
mutex_lock(&sev_cmd_mutex);
__sev_firmware_shutdown(sev, false);
kfree(sev->tio_status);
sev->tio_status = NULL;
mutex_unlock(&sev_cmd_mutex);
}

View File

@@ -34,6 +34,8 @@ struct sev_misc_dev {
struct miscdevice misc;
};
struct sev_tio_status;
struct sev_device {
struct device *dev;
struct psp_device *psp;
@@ -61,15 +63,24 @@ struct sev_device {
struct sev_user_data_snp_status snp_plat_status;
struct snp_feature_info snp_feat_info_0;
struct tsm_dev *tsmdev;
struct sev_tio_status *tio_status;
};
int sev_dev_init(struct psp_device *psp);
void sev_dev_destroy(struct psp_device *psp);
int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret);
void sev_pci_init(void);
void sev_pci_exit(void);
struct page *snp_alloc_hv_fixed_pages(unsigned int num_2mb_pages);
void snp_free_hv_fixed_pages(struct page *page);
void sev_tsm_init_locked(struct sev_device *sev, void *tio_status_page);
void sev_tsm_uninit(struct sev_device *sev);
int sev_tio_cmd_buffer_len(int cmd);
#endif /* __SEV_DEV_H */

View File

@@ -1,18 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2025 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/sprintf.h>
#include <linux/string_helpers.h>
#include "adf_pm_dbgfs_utils.h"
/*
* This is needed because a variable is used to index the mask at
* pm_scnprint_table(), making it not compile time constant, so the compile
* asserts from FIELD_GET() or u32_get_bits() won't be fulfilled.
*/
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define PM_INFO_MAX_KEY_LEN 21
static int pm_scnprint_table(char *buff, const struct pm_status_row *table,

View File

@@ -44,6 +44,7 @@
* but lo_hi_readq() ensures that we are safe across all e3-1200 processors.
*/
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
@@ -139,9 +140,6 @@
#define IE31200_CAPID0_DDPCD BIT(6)
#define IE31200_CAPID0_ECC BIT(1)
/* Non-constant mask variant of FIELD_GET() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
static int nr_channels;
static struct pci_dev *mci_pdev;
static int ie31200_registered = 1;

View File

@@ -5,6 +5,7 @@
* Joel Stanley <joel@jms.id.au>
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/gpio/aspeed.h>
@@ -30,10 +31,6 @@
*/
#include <linux/gpio/consumer.h>
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
#define GPIO_G7_IRQ_STS_BASE 0x100
#define GPIO_G7_IRQ_STS_OFFSET(x) (GPIO_G7_IRQ_STS_BASE + (x) * 0x4)
#define GPIO_G7_CTRL_REG_BASE 0x180

View File

@@ -53,9 +53,6 @@
#define AD3530R_MAX_CHANNELS 8
#define AD3531R_MAX_CHANNELS 4
/* Non-constant mask variant of FIELD_PREP() */
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
enum ad3530r_mode {
AD3530R_NORMAL_OP,
AD3530R_POWERDOWN_1K,

View File

@@ -22,6 +22,7 @@
* the "wakeup" GPIO is not given, power management will be disabled.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
@@ -68,10 +69,6 @@
#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
#define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
struct mlx_chip_info {
/* EEPROM offsets with 16-bit data, MSB first */
/* emissivity correction coefficient */

View File

@@ -107,6 +107,7 @@
/* Extended Feature 2 Bits */
#define FEATURE_SEVSNPIO_SUP BIT_ULL(1)
#define FEATURE_SNPAVICSUP GENMASK_ULL(7, 5)
#define FEATURE_SNPAVICSUP_GAM(x) \
(FIELD_GET(FEATURE_SNPAVICSUP, x) == 0x1)

View File

@@ -2261,6 +2261,9 @@ static void print_iommu_info(void)
if (check_feature(FEATURE_SNP))
pr_cont(" SNP");
if (check_feature2(FEATURE_SEVSNPIO_SUP))
pr_cont(" SEV-TIO");
pr_cont("\n");
}
@@ -4028,4 +4031,10 @@ int amd_iommu_snp_disable(void)
return 0;
}
EXPORT_SYMBOL_GPL(amd_iommu_snp_disable);
bool amd_iommu_sev_tio_supported(void)
{
return check_feature2(FEATURE_SEVSNPIO_SUP);
}
EXPORT_SYMBOL_GPL(amd_iommu_sev_tio_supported);
#endif

View File

@@ -10,7 +10,7 @@
/* driver definitions */
#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>"
#define DRIVER_CARD "Silicon Labs Si470x FM Radio"
#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
#define DRIVER_VERSION "1.0.2"

View File

@@ -70,12 +70,12 @@
#include "ts2020.h"
#define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw";
#define LME2510_C_LG "dvb-usb-lme2510c-lg.fw";
#define LME2510_C_S0194 "dvb-usb-lme2510c-s0194.fw";
#define LME2510_C_RS2000 "dvb-usb-lme2510c-rs2000.fw";
#define LME2510_LG "dvb-usb-lme2510-lg.fw";
#define LME2510_S0194 "dvb-usb-lme2510-s0194.fw";
#define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw"
#define LME2510_C_LG "dvb-usb-lme2510c-lg.fw"
#define LME2510_C_S0194 "dvb-usb-lme2510c-s0194.fw"
#define LME2510_C_RS2000 "dvb-usb-lme2510c-rs2000.fw"
#define LME2510_LG "dvb-usb-lme2510-lg.fw"
#define LME2510_S0194 "dvb-usb-lme2510-s0194.fw"
/* debug */
static int dvb_usb_lme2510_debug;

View File

@@ -97,6 +97,25 @@ config OF_PMEM
Select Y if unsure.
config RAMDAX
tristate "Support persistent memory interfaces on RAM carveouts"
depends on X86_PMEM_LEGACY || OF || COMPILE_TEST
default LIBNVDIMM
help
Allows creation of DAX devices on RAM carveouts.
Memory ranges that are manually specified by the
'memmap=nn[KMG]!ss[KMG]' kernel command line or defined by dummy
pmem-region device tree nodes would be managed by this driver as DIMM
devices with support for dynamic layout of namespaces.
The driver steals 128K in the end of the memmap range for the
namespace management. This allows supporting up to 509 namespaces
(see 'ndctl create-namespace --help').
The driver should be force bound to e820_pmem or pmem-region platform
devices using 'driver_override' device attribute.
Select N if unsure.
config NVDIMM_KEYS
def_bool y
depends on ENCRYPTED_KEYS

View File

@@ -5,6 +5,7 @@ obj-$(CONFIG_ND_BTT) += nd_btt.o
obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
obj-$(CONFIG_OF_PMEM) += of_pmem.o
obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o
obj-$(CONFIG_RAMDAX) += ramdax.o
nd_pmem-y := pmem.o

282
drivers/nvdimm/ramdax.c Normal file
View File

@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025, Mike Rapoport, Microsoft
*
* Based on e820 pmem driver:
* Copyright (c) 2015, Christoph Hellwig.
* Copyright (c) 2015, Intel Corporation.
*/
#include <linux/platform_device.h>
#include <linux/memory_hotplug.h>
#include <linux/libnvdimm.h>
#include <linux/module.h>
#include <linux/numa.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <uapi/linux/ndctl.h>
#define LABEL_AREA_SIZE SZ_128K
struct ramdax_dimm {
struct nvdimm *nvdimm;
void *label_area;
};
static void ramdax_remove(struct platform_device *pdev)
{
struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev);
nvdimm_bus_unregister(nvdimm_bus);
}
static int ramdax_register_region(struct resource *res,
struct nvdimm *nvdimm,
struct nvdimm_bus *nvdimm_bus)
{
struct nd_mapping_desc mapping;
struct nd_region_desc ndr_desc;
struct nd_interleave_set *nd_set;
int nid = phys_to_target_node(res->start);
nd_set = kzalloc(sizeof(*nd_set), GFP_KERNEL);
if (!nd_set)
return -ENOMEM;
nd_set->cookie1 = 0xcafebeefcafebeef;
nd_set->cookie2 = nd_set->cookie1;
nd_set->altcookie = nd_set->cookie1;
memset(&mapping, 0, sizeof(mapping));
mapping.nvdimm = nvdimm;
mapping.start = 0;
mapping.size = resource_size(res) - LABEL_AREA_SIZE;
memset(&ndr_desc, 0, sizeof(ndr_desc));
ndr_desc.res = res;
ndr_desc.numa_node = numa_map_to_online_node(nid);
ndr_desc.target_node = nid;
ndr_desc.num_mappings = 1;
ndr_desc.mapping = &mapping;
ndr_desc.nd_set = nd_set;
if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc))
goto err_free_nd_set;
return 0;
err_free_nd_set:
kfree(nd_set);
return -ENXIO;
}
static int ramdax_register_dimm(struct resource *res, void *data)
{
resource_size_t start = res->start;
resource_size_t size = resource_size(res);
unsigned long flags = 0, cmd_mask = 0;
struct nvdimm_bus *nvdimm_bus = data;
struct ramdax_dimm *dimm;
int err;
dimm = kzalloc(sizeof(*dimm), GFP_KERNEL);
if (!dimm)
return -ENOMEM;
dimm->label_area = memremap(start + size - LABEL_AREA_SIZE,
LABEL_AREA_SIZE, MEMREMAP_WB);
if (!dimm->label_area) {
err = -ENOMEM;
goto err_free_dimm;
}
set_bit(NDD_LABELING, &flags);
set_bit(NDD_REGISTER_SYNC, &flags);
set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
dimm->nvdimm = nvdimm_create(nvdimm_bus, dimm,
/* dimm_attribute_groups */ NULL,
flags, cmd_mask, 0, NULL);
if (!dimm->nvdimm) {
err = -ENOMEM;
goto err_unmap_label;
}
err = ramdax_register_region(res, dimm->nvdimm, nvdimm_bus);
if (err)
goto err_remove_nvdimm;
return 0;
err_remove_nvdimm:
nvdimm_delete(dimm->nvdimm);
err_unmap_label:
memunmap(dimm->label_area);
err_free_dimm:
kfree(dimm);
return err;
}
static int ramdax_get_config_size(struct nvdimm *nvdimm, int buf_len,
struct nd_cmd_get_config_size *cmd)
{
if (sizeof(*cmd) > buf_len)
return -EINVAL;
*cmd = (struct nd_cmd_get_config_size){
.status = 0,
.config_size = LABEL_AREA_SIZE,
.max_xfer = 8,
};
return 0;
}
static int ramdax_get_config_data(struct nvdimm *nvdimm, int buf_len,
struct nd_cmd_get_config_data_hdr *cmd)
{
struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
if (sizeof(*cmd) > buf_len)
return -EINVAL;
if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
return -EINVAL;
if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
return -EINVAL;
memcpy(cmd->out_buf, dimm->label_area + cmd->in_offset, cmd->in_length);
return 0;
}
static int ramdax_set_config_data(struct nvdimm *nvdimm, int buf_len,
struct nd_cmd_set_config_hdr *cmd)
{
struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
if (sizeof(*cmd) > buf_len)
return -EINVAL;
if (struct_size(cmd, in_buf, cmd->in_length) > buf_len)
return -EINVAL;
if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
return -EINVAL;
memcpy(dimm->label_area + cmd->in_offset, cmd->in_buf, cmd->in_length);
return 0;
}
static int ramdax_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
void *buf, unsigned int buf_len)
{
unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
if (!test_bit(cmd, &cmd_mask))
return -ENOTTY;
switch (cmd) {
case ND_CMD_GET_CONFIG_SIZE:
return ramdax_get_config_size(nvdimm, buf_len, buf);
case ND_CMD_GET_CONFIG_DATA:
return ramdax_get_config_data(nvdimm, buf_len, buf);
case ND_CMD_SET_CONFIG_DATA:
return ramdax_set_config_data(nvdimm, buf_len, buf);
default:
return -ENOTTY;
}
}
static int ramdax_ctl(struct nvdimm_bus_descriptor *nd_desc,
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
{
/*
* No firmware response to translate, let the transport error
* code take precedence.
*/
*cmd_rc = 0;
if (!nvdimm)
return -ENOTTY;
return ramdax_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
}
#ifdef CONFIG_OF
static const struct of_device_id ramdax_of_matches[] = {
{ .compatible = "pmem-region", },
{ },
};
#endif
static int ramdax_probe_of(struct platform_device *pdev,
struct nvdimm_bus *bus, struct device_node *np)
{
int err;
if (!of_match_node(ramdax_of_matches, np))
return -ENODEV;
for (int i = 0; i < pdev->num_resources; i++) {
err = ramdax_register_dimm(&pdev->resource[i], bus);
if (err)
goto err_unregister;
}
return 0;
err_unregister:
/*
* FIXME: should we unregister the dimms that were registered
* successfully
*/
return err;
}
static int ramdax_probe(struct platform_device *pdev)
{
static struct nvdimm_bus_descriptor nd_desc;
struct device *dev = &pdev->dev;
struct nvdimm_bus *nvdimm_bus;
struct device_node *np;
int rc = -ENXIO;
nd_desc.provider_name = "ramdax";
nd_desc.module = THIS_MODULE;
nd_desc.ndctl = ramdax_ctl;
nvdimm_bus = nvdimm_bus_register(dev, &nd_desc);
if (!nvdimm_bus)
goto err;
np = dev_of_node(&pdev->dev);
if (np)
rc = ramdax_probe_of(pdev, nvdimm_bus, np);
else
rc = walk_iomem_res_desc(IORES_DESC_PERSISTENT_MEMORY_LEGACY,
IORESOURCE_MEM, 0, -1, nvdimm_bus,
ramdax_register_dimm);
if (rc)
goto err;
platform_set_drvdata(pdev, nvdimm_bus);
return 0;
err:
nvdimm_bus_unregister(nvdimm_bus);
return rc;
}
static struct platform_driver ramdax_driver = {
.probe = ramdax_probe,
.remove = ramdax_remove,
.driver = {
.name = "ramdax",
},
};
module_platform_driver(ramdax_driver);
MODULE_DESCRIPTION("NVDIMM support for e820 type-12 memory and OF pmem-region");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Microsoft Corporation");

View File

@@ -424,7 +424,7 @@ static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
* query.
*/
get_device(dev);
queue_delayed_work(system_wq, &nvdimm->dwork, 0);
queue_delayed_work(system_percpu_wq, &nvdimm->dwork, 0);
}
return rc;
@@ -457,7 +457,7 @@ static void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
/* setup delayed work again */
tmo += 10;
queue_delayed_work(system_wq, &nvdimm->dwork, tmo * HZ);
queue_delayed_work(system_percpu_wq, &nvdimm->dwork, tmo * HZ);
nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo);
return;
}

View File

@@ -517,10 +517,10 @@ static u32 hint_lookup[] = {
* ccio_io_pdir_entry - Initialize an I/O Pdir.
* @pdir_ptr: A pointer into I/O Pdir.
* @sid: The Space Identifier.
* @vba: The virtual address.
* @pba: The physical address.
* @hints: The DMA Hint.
*
* Given a virtual address (vba, arg2) and space id, (sid, arg1),
* Given a physical address (pba, arg2) and space id, (sid, arg1),
* load the I/O PDIR entry pointed to by pdir_ptr (arg0). Each IO Pdir
* entry consists of 8 bytes as shown below (MSB == bit 0):
*
@@ -543,7 +543,7 @@ static u32 hint_lookup[] = {
* index are bits 12:19 of the value returned by LCI.
*/
static void
ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba,
ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, phys_addr_t pba,
unsigned long hints)
{
register unsigned long pa;
@@ -557,7 +557,7 @@ ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba,
** "hints" parm includes the VALID bit!
** "dep" clobbers the physical address offset bits as well.
*/
pa = lpa(vba);
pa = pba;
asm volatile("depw %1,31,12,%0" : "+r" (pa) : "r" (hints));
((u32 *)pdir_ptr)[1] = (u32) pa;
@@ -582,7 +582,7 @@ ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba,
** Grab virtual index [0:11]
** Deposit virt_idx bits into I/O PDIR word
*/
asm volatile ("lci %%r0(%1), %0" : "=r" (ci) : "r" (vba));
asm volatile ("lci %%r0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pba)));
asm volatile ("extru %1,19,12,%0" : "+r" (ci) : "r" (ci));
asm volatile ("depw %1,15,12,%0" : "+r" (pa) : "r" (ci));
@@ -704,14 +704,14 @@ ccio_dma_supported(struct device *dev, u64 mask)
/**
* ccio_map_single - Map an address range into the IOMMU.
* @dev: The PCI device.
* @addr: The start address of the DMA region.
* @addr: The physical address of the DMA region.
* @size: The length of the DMA region.
* @direction: The direction of the DMA transaction (to/from device).
*
* This function implements the pci_map_single function.
*/
static dma_addr_t
ccio_map_single(struct device *dev, void *addr, size_t size,
ccio_map_single(struct device *dev, phys_addr_t addr, size_t size,
enum dma_data_direction direction)
{
int idx;
@@ -730,7 +730,7 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
BUG_ON(size <= 0);
/* save offset bits */
offset = ((unsigned long) addr) & ~IOVP_MASK;
offset = offset_in_page(addr);
/* round up to nearest IOVP_SIZE */
size = ALIGN(size + offset, IOVP_SIZE);
@@ -746,15 +746,15 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
pdir_start = &(ioc->pdir_base[idx]);
DBG_RUN("%s() %px -> %#lx size: %zu\n",
__func__, addr, (long)(iovp | offset), size);
DBG_RUN("%s() %pa -> %#lx size: %zu\n",
__func__, &addr, (long)(iovp | offset), size);
/* If not cacheline aligned, force SAFE_DMA on the whole mess */
if((size % L1_CACHE_BYTES) || ((unsigned long)addr % L1_CACHE_BYTES))
if ((size % L1_CACHE_BYTES) || (addr % L1_CACHE_BYTES))
hint |= HINT_SAFE_DMA;
while(size > 0) {
ccio_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long)addr, hint);
ccio_io_pdir_entry(pdir_start, KERNEL_SPACE, addr, hint);
DBG_RUN(" pdir %p %08x%08x\n",
pdir_start,
@@ -773,17 +773,18 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
static dma_addr_t
ccio_map_page(struct device *dev, struct page *page, unsigned long offset,
size_t size, enum dma_data_direction direction,
unsigned long attrs)
ccio_map_phys(struct device *dev, phys_addr_t phys, size_t size,
enum dma_data_direction direction, unsigned long attrs)
{
return ccio_map_single(dev, page_address(page) + offset, size,
direction);
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
return ccio_map_single(dev, phys, size, direction);
}
/**
* ccio_unmap_page - Unmap an address range from the IOMMU.
* ccio_unmap_phys - Unmap an address range from the IOMMU.
* @dev: The PCI device.
* @iova: The start address of the DMA region.
* @size: The length of the DMA region.
@@ -791,7 +792,7 @@ ccio_map_page(struct device *dev, struct page *page, unsigned long offset,
* @attrs: attributes
*/
static void
ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
ccio_unmap_phys(struct device *dev, dma_addr_t iova, size_t size,
enum dma_data_direction direction, unsigned long attrs)
{
struct ioc *ioc;
@@ -853,7 +854,8 @@ ccio_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag,
if (ret) {
memset(ret, 0, size);
*dma_handle = ccio_map_single(dev, ret, size, DMA_BIDIRECTIONAL);
*dma_handle = ccio_map_single(dev, virt_to_phys(ret), size,
DMA_BIDIRECTIONAL);
}
return ret;
@@ -873,7 +875,7 @@ static void
ccio_free(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle, unsigned long attrs)
{
ccio_unmap_page(dev, dma_handle, size, 0, 0);
ccio_unmap_phys(dev, dma_handle, size, 0, 0);
free_pages((unsigned long)cpu_addr, get_order(size));
}
@@ -920,7 +922,7 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
/* Fast path single entry scatterlists. */
if (nents == 1) {
sg_dma_address(sglist) = ccio_map_single(dev,
sg_virt(sglist), sglist->length,
sg_phys(sglist), sglist->length,
direction);
sg_dma_len(sglist) = sglist->length;
return 1;
@@ -1004,7 +1006,7 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
#ifdef CCIO_COLLECT_STATS
ioc->usg_pages += sg_dma_len(sglist) >> PAGE_SHIFT;
#endif
ccio_unmap_page(dev, sg_dma_address(sglist),
ccio_unmap_phys(dev, sg_dma_address(sglist),
sg_dma_len(sglist), direction, 0);
++sglist;
nents--;
@@ -1017,8 +1019,8 @@ static const struct dma_map_ops ccio_ops = {
.dma_supported = ccio_dma_supported,
.alloc = ccio_alloc,
.free = ccio_free,
.map_page = ccio_map_page,
.unmap_page = ccio_unmap_page,
.map_phys = ccio_map_phys,
.unmap_phys = ccio_unmap_phys,
.map_sg = ccio_map_sg,
.unmap_sg = ccio_unmap_sg,
.get_sgtable = dma_common_get_sgtable,
@@ -1072,7 +1074,7 @@ static int ccio_proc_info(struct seq_file *m, void *p)
ioc->msingle_calls, ioc->msingle_pages,
(int)((ioc->msingle_pages * 1000)/ioc->msingle_calls));
/* KLUGE - unmap_sg calls unmap_page for each mapped page */
/* KLUGE - unmap_sg calls unmap_phys for each mapped page */
min = ioc->usingle_calls - ioc->usg_calls;
max = ioc->usingle_pages - ioc->usg_pages;
seq_printf(m, "pci_unmap_single: %8ld calls %8ld pages (avg %d/1000)\n",

View File

@@ -14,7 +14,7 @@
static inline unsigned int
iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents,
unsigned long hint,
void (*iommu_io_pdir_entry)(__le64 *, space_t, unsigned long,
void (*iommu_io_pdir_entry)(__le64 *, space_t, phys_addr_t,
unsigned long))
{
struct scatterlist *dma_sg = startsg; /* pointer to current DMA */
@@ -28,7 +28,7 @@ iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents,
dma_sg--;
while (nents-- > 0) {
unsigned long vaddr;
phys_addr_t paddr;
long size;
DBG_RUN_SG(" %d : %08lx %p/%05x\n", nents,
@@ -67,7 +67,7 @@ iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents,
BUG_ON(pdirp == NULL);
vaddr = (unsigned long)sg_virt(startsg);
paddr = sg_phys(startsg);
sg_dma_len(dma_sg) += startsg->length;
size = startsg->length + dma_offset;
dma_offset = 0;
@@ -76,8 +76,8 @@ iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents,
#endif
do {
iommu_io_pdir_entry(pdirp, KERNEL_SPACE,
vaddr, hint);
vaddr += IOVP_SIZE;
paddr, hint);
paddr += IOVP_SIZE;
size -= IOVP_SIZE;
pdirp++;
} while(unlikely(size > 0));

View File

@@ -532,7 +532,7 @@ typedef unsigned long space_t;
* sba_io_pdir_entry - fill in one IO PDIR entry
* @pdir_ptr: pointer to IO PDIR entry
* @sid: process Space ID - currently only support KERNEL_SPACE
* @vba: Virtual CPU address of buffer to map
* @pba: Physical address of buffer to map
* @hint: DMA hint set to use for this mapping
*
* SBA Mapping Routine
@@ -569,20 +569,17 @@ typedef unsigned long space_t;
*/
static void
sba_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba,
sba_io_pdir_entry(__le64 *pdir_ptr, space_t sid, phys_addr_t pba,
unsigned long hint)
{
u64 pa; /* physical address */
register unsigned ci; /* coherent index */
pa = lpa(vba);
pa &= IOVP_MASK;
asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pba)));
pba &= IOVP_MASK;
pba |= (ci >> PAGE_SHIFT) & 0xff; /* move CI (8 bits) into lowest byte */
asm("lci 0(%1), %0" : "=r" (ci) : "r" (vba));
pa |= (ci >> PAGE_SHIFT) & 0xff; /* move CI (8 bits) into lowest byte */
pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */
*pdir_ptr = cpu_to_le64(pa); /* swap and store into I/O Pdir */
pba |= SBA_PDIR_VALID_BIT; /* set "valid" bit */
*pdir_ptr = cpu_to_le64(pba); /* swap and store into I/O Pdir */
/*
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set
@@ -707,7 +704,7 @@ static int sba_dma_supported( struct device *dev, u64 mask)
* See Documentation/core-api/dma-api-howto.rst
*/
static dma_addr_t
sba_map_single(struct device *dev, void *addr, size_t size,
sba_map_single(struct device *dev, phys_addr_t addr, size_t size,
enum dma_data_direction direction)
{
struct ioc *ioc;
@@ -722,7 +719,7 @@ sba_map_single(struct device *dev, void *addr, size_t size,
return DMA_MAPPING_ERROR;
/* save offset bits */
offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
offset = offset_in_page(addr);
/* round up to nearest IOVP_SIZE */
size = (size + offset + ~IOVP_MASK) & IOVP_MASK;
@@ -739,13 +736,13 @@ sba_map_single(struct device *dev, void *addr, size_t size,
pide = sba_alloc_range(ioc, dev, size);
iovp = (dma_addr_t) pide << IOVP_SHIFT;
DBG_RUN("%s() 0x%p -> 0x%lx\n",
__func__, addr, (long) iovp | offset);
DBG_RUN("%s() 0x%pa -> 0x%lx\n",
__func__, &addr, (long) iovp | offset);
pdir_start = &(ioc->pdir_base[pide]);
while (size > 0) {
sba_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long) addr, 0);
sba_io_pdir_entry(pdir_start, KERNEL_SPACE, addr, 0);
DBG_RUN(" pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n",
pdir_start,
@@ -778,17 +775,18 @@ sba_map_single(struct device *dev, void *addr, size_t size,
static dma_addr_t
sba_map_page(struct device *dev, struct page *page, unsigned long offset,
size_t size, enum dma_data_direction direction,
unsigned long attrs)
sba_map_phys(struct device *dev, phys_addr_t phys, size_t size,
enum dma_data_direction direction, unsigned long attrs)
{
return sba_map_single(dev, page_address(page) + offset, size,
direction);
if (unlikely(attrs & DMA_ATTR_MMIO))
return DMA_MAPPING_ERROR;
return sba_map_single(dev, phys, size, direction);
}
/**
* sba_unmap_page - unmap one IOVA and free resources
* sba_unmap_phys - unmap one IOVA and free resources
* @dev: instance of PCI owned by the driver that's asking.
* @iova: IOVA of driver buffer previously mapped.
* @size: number of bytes mapped in driver buffer.
@@ -798,7 +796,7 @@ sba_map_page(struct device *dev, struct page *page, unsigned long offset,
* See Documentation/core-api/dma-api-howto.rst
*/
static void
sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
sba_unmap_phys(struct device *dev, dma_addr_t iova, size_t size,
enum dma_data_direction direction, unsigned long attrs)
{
struct ioc *ioc;
@@ -893,7 +891,7 @@ static void *sba_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_handle
if (ret) {
memset(ret, 0, size);
*dma_handle = sba_map_single(hwdev, ret, size, 0);
*dma_handle = sba_map_single(hwdev, virt_to_phys(ret), size, 0);
}
return ret;
@@ -914,7 +912,7 @@ static void
sba_free(struct device *hwdev, size_t size, void *vaddr,
dma_addr_t dma_handle, unsigned long attrs)
{
sba_unmap_page(hwdev, dma_handle, size, 0, 0);
sba_unmap_phys(hwdev, dma_handle, size, 0, 0);
free_pages((unsigned long) vaddr, get_order(size));
}
@@ -962,7 +960,7 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
/* Fast path single entry scatterlists. */
if (nents == 1) {
sg_dma_address(sglist) = sba_map_single(dev, sg_virt(sglist),
sg_dma_address(sglist) = sba_map_single(dev, sg_phys(sglist),
sglist->length, direction);
sg_dma_len(sglist) = sglist->length;
return 1;
@@ -1061,7 +1059,7 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
while (nents && sg_dma_len(sglist)) {
sba_unmap_page(dev, sg_dma_address(sglist), sg_dma_len(sglist),
sba_unmap_phys(dev, sg_dma_address(sglist), sg_dma_len(sglist),
direction, 0);
#ifdef SBA_COLLECT_STATS
ioc->usg_pages += ((sg_dma_address(sglist) & ~IOVP_MASK) + sg_dma_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT;
@@ -1085,8 +1083,8 @@ static const struct dma_map_ops sba_ops = {
.dma_supported = sba_dma_supported,
.alloc = sba_alloc,
.free = sba_free,
.map_page = sba_map_page,
.unmap_page = sba_unmap_page,
.map_phys = sba_map_phys,
.unmap_phys = sba_unmap_phys,
.map_sg = sba_map_sg,
.unmap_sg = sba_unmap_sg,
.get_sgtable = dma_common_get_sgtable,

View File

@@ -122,6 +122,24 @@ config XEN_PCIDEV_FRONTEND
config PCI_ATS
bool
config PCI_IDE
bool
config PCI_TSM
bool "PCI TSM: Device security protocol support"
select PCI_IDE
select PCI_DOE
select TSM
help
The TEE (Trusted Execution Environment) Device Interface
Security Protocol (TDISP) defines a "TSM" as a platform agent
that manages device authentication, link encryption, link
integrity protection, and assignment of PCI device functions
(virtual or physical) to confidential computing VMs that can
access (DMA) guest private memory.
Enable a platform TSM driver to use this capability.
config PCI_DOE
bool "Enable PCI Data Object Exchange (DOE) support"
help

View File

@@ -34,6 +34,8 @@ obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
obj-$(CONFIG_VGA_ARB) += vgaarb.o
obj-$(CONFIG_PCI_DOE) += doe.o
obj-$(CONFIG_PCI_IDE) += ide.o
obj-$(CONFIG_PCI_TSM) += tsm.o
obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
obj-$(CONFIG_PCI_NPEM) += npem.o
obj-$(CONFIG_PCIE_TPH) += tph.o

View File

@@ -8,6 +8,7 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cleanup.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
@@ -435,6 +436,27 @@ static int __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void
return ret;
}
static int __pci_walk_bus_reverse(struct pci_bus *top,
int (*cb)(struct pci_dev *, void *),
void *userdata)
{
struct pci_dev *dev;
int ret = 0;
list_for_each_entry_reverse(dev, &top->devices, bus_list) {
if (dev->subordinate) {
ret = __pci_walk_bus_reverse(dev->subordinate, cb,
userdata);
if (ret)
break;
}
ret = cb(dev, userdata);
if (ret)
break;
}
return ret;
}
/**
* pci_walk_bus - walk devices on/under bus, calling callback.
* @top: bus whose devices should be walked
@@ -456,6 +478,23 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void
}
EXPORT_SYMBOL_GPL(pci_walk_bus);
/**
* pci_walk_bus_reverse - walk devices on/under bus, calling callback.
* @top: bus whose devices should be walked
* @cb: callback to be called for each device found
* @userdata: arbitrary pointer to be passed to callback
*
* Same semantics as pci_walk_bus(), but walks the bus in reverse order.
*/
void pci_walk_bus_reverse(struct pci_bus *top,
int (*cb)(struct pci_dev *, void *), void *userdata)
{
down_read(&pci_bus_sem);
__pci_walk_bus_reverse(top, cb, userdata);
up_read(&pci_bus_sem);
}
EXPORT_SYMBOL_GPL(pci_walk_bus_reverse);
void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
{
lockdep_assert_held(&pci_bus_sem);

View File

@@ -24,8 +24,6 @@
#include "pci.h"
#define PCI_DOE_FEATURE_DISCOVERY 0
/* Timeout of 1 second from 6.30.2 Operation, PCI Spec r6.0 */
#define PCI_DOE_TIMEOUT HZ
#define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128)

815
drivers/pci/ide.c Normal file
View File

@@ -0,0 +1,815 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
/* PCIe r7.0 section 6.33 Integrity & Data Encryption (IDE) */
#define dev_fmt(fmt) "PCI/IDE: " fmt
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/pci-ide.h>
#include <linux/pci_regs.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/tsm.h>
#include "pci.h"
static int __sel_ide_offset(u16 ide_cap, u8 nr_link_ide, u8 stream_index,
u8 nr_ide_mem)
{
u32 offset = ide_cap + PCI_IDE_LINK_STREAM_0 +
nr_link_ide * PCI_IDE_LINK_BLOCK_SIZE;
/*
* Assume a constant number of address association resources per stream
* index
*/
return offset + stream_index * PCI_IDE_SEL_BLOCK_SIZE(nr_ide_mem);
}
static int sel_ide_offset(struct pci_dev *pdev,
struct pci_ide_partner *settings)
{
return __sel_ide_offset(pdev->ide_cap, pdev->nr_link_ide,
settings->stream_index, pdev->nr_ide_mem);
}
static bool reserve_stream_index(struct pci_dev *pdev, u8 idx)
{
int ret;
ret = ida_alloc_range(&pdev->ide_stream_ida, idx, idx, GFP_KERNEL);
return ret >= 0;
}
static bool reserve_stream_id(struct pci_host_bridge *hb, u8 id)
{
int ret;
ret = ida_alloc_range(&hb->ide_stream_ids_ida, id, id, GFP_KERNEL);
return ret >= 0;
}
static bool claim_stream(struct pci_host_bridge *hb, u8 stream_id,
struct pci_dev *pdev, u8 stream_idx)
{
dev_info(&hb->dev, "Stream ID %d active at init\n", stream_id);
if (!reserve_stream_id(hb, stream_id)) {
dev_info(&hb->dev, "Failed to claim %s Stream ID %d\n",
stream_id == PCI_IDE_RESERVED_STREAM_ID ? "reserved" :
"active",
stream_id);
return false;
}
/* No stream index to reserve in the Link IDE case */
if (!pdev)
return true;
if (!reserve_stream_index(pdev, stream_idx)) {
pci_info(pdev, "Failed to claim active Selective Stream %d\n",
stream_idx);
return false;
}
return true;
}
void pci_ide_init(struct pci_dev *pdev)
{
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
u16 nr_link_ide, nr_ide_mem, nr_streams;
u16 ide_cap;
u32 val;
/*
* Unconditionally init so that ida idle state is consistent with
* pdev->ide_cap.
*/
ida_init(&pdev->ide_stream_ida);
if (!pci_is_pcie(pdev))
return;
ide_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_IDE);
if (!ide_cap)
return;
pci_read_config_dword(pdev, ide_cap + PCI_IDE_CAP, &val);
if ((val & PCI_IDE_CAP_SELECTIVE) == 0)
return;
/*
* Require endpoint IDE capability to be paired with IDE Root Port IDE
* capability.
*/
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT) {
struct pci_dev *rp = pcie_find_root_port(pdev);
if (!rp->ide_cap)
return;
}
pdev->ide_cfg = FIELD_GET(PCI_IDE_CAP_SEL_CFG, val);
pdev->ide_tee_limit = FIELD_GET(PCI_IDE_CAP_TEE_LIMITED, val);
if (val & PCI_IDE_CAP_LINK)
nr_link_ide = 1 + FIELD_GET(PCI_IDE_CAP_LINK_TC_NUM, val);
else
nr_link_ide = 0;
nr_ide_mem = 0;
nr_streams = 1 + FIELD_GET(PCI_IDE_CAP_SEL_NUM, val);
for (u16 i = 0; i < nr_streams; i++) {
int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem);
int nr_assoc;
u32 val;
u8 id;
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val);
/*
* Let's not entertain streams that do not have a constant
* number of address association blocks
*/
nr_assoc = FIELD_GET(PCI_IDE_SEL_CAP_ASSOC_NUM, val);
if (i && (nr_assoc != nr_ide_mem)) {
pci_info(pdev, "Unsupported Selective Stream %d capability, SKIP the rest\n", i);
nr_streams = i;
break;
}
nr_ide_mem = nr_assoc;
/*
* Claim Stream IDs and Selective Stream blocks that are already
* active on the device
*/
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val);
id = FIELD_GET(PCI_IDE_SEL_CTL_ID, val);
if ((val & PCI_IDE_SEL_CTL_EN) &&
!claim_stream(hb, id, pdev, i))
return;
}
/* Reserve link stream-ids that are already active on the device */
for (u16 i = 0; i < nr_link_ide; ++i) {
int pos = ide_cap + PCI_IDE_LINK_STREAM_0 + i * PCI_IDE_LINK_BLOCK_SIZE;
u8 id;
pci_read_config_dword(pdev, pos + PCI_IDE_LINK_CTL_0, &val);
id = FIELD_GET(PCI_IDE_LINK_CTL_ID, val);
if ((val & PCI_IDE_LINK_CTL_EN) &&
!claim_stream(hb, id, NULL, -1))
return;
}
for (u16 i = 0; i < nr_streams; i++) {
int pos = __sel_ide_offset(ide_cap, nr_link_ide, i, nr_ide_mem);
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CAP, &val);
if (val & PCI_IDE_SEL_CTL_EN)
continue;
val &= ~PCI_IDE_SEL_CTL_ID;
val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
}
for (u16 i = 0; i < nr_link_ide; ++i) {
int pos = ide_cap + PCI_IDE_LINK_STREAM_0 +
i * PCI_IDE_LINK_BLOCK_SIZE;
pci_read_config_dword(pdev, pos, &val);
if (val & PCI_IDE_LINK_CTL_EN)
continue;
val &= ~PCI_IDE_LINK_CTL_ID;
val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
pci_write_config_dword(pdev, pos, val);
}
pdev->ide_cap = ide_cap;
pdev->nr_link_ide = nr_link_ide;
pdev->nr_sel_ide = nr_streams;
pdev->nr_ide_mem = nr_ide_mem;
}
struct stream_index {
struct ida *ida;
u8 stream_index;
};
static void free_stream_index(struct stream_index *stream)
{
ida_free(stream->ida, stream->stream_index);
}
DEFINE_FREE(free_stream, struct stream_index *, if (_T) free_stream_index(_T))
static struct stream_index *alloc_stream_index(struct ida *ida, u16 max,
struct stream_index *stream)
{
int id;
if (!max)
return NULL;
id = ida_alloc_max(ida, max - 1, GFP_KERNEL);
if (id < 0)
return NULL;
*stream = (struct stream_index) {
.ida = ida,
.stream_index = id,
};
return stream;
}
/**
* pci_ide_stream_alloc() - Reserve stream indices and probe for settings
* @pdev: IDE capable PCIe Endpoint Physical Function
*
* Retrieve the Requester ID range of @pdev for programming its Root
* Port IDE RID Association registers, and conversely retrieve the
* Requester ID of the Root Port for programming @pdev's IDE RID
* Association registers.
*
* Allocate a Selective IDE Stream Register Block instance per port.
*
* Allocate a platform stream resource from the associated host bridge.
* Retrieve stream association parameters for Requester ID range and
* address range restrictions for the stream.
*/
struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev)
{
/* EP, RP, + HB Stream allocation */
struct stream_index __stream[PCI_IDE_HB + 1];
struct pci_bus_region pref_assoc = { 0, -1 };
struct pci_bus_region mem_assoc = { 0, -1 };
struct resource *mem, *pref;
struct pci_host_bridge *hb;
struct pci_dev *rp, *br;
int num_vf, rid_end;
if (!pci_is_pcie(pdev))
return NULL;
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
return NULL;
if (!pdev->ide_cap)
return NULL;
struct pci_ide *ide __free(kfree) = kzalloc(sizeof(*ide), GFP_KERNEL);
if (!ide)
return NULL;
hb = pci_find_host_bridge(pdev->bus);
struct stream_index *hb_stream __free(free_stream) = alloc_stream_index(
&hb->ide_stream_ida, hb->nr_ide_streams, &__stream[PCI_IDE_HB]);
if (!hb_stream)
return NULL;
rp = pcie_find_root_port(pdev);
struct stream_index *rp_stream __free(free_stream) = alloc_stream_index(
&rp->ide_stream_ida, rp->nr_sel_ide, &__stream[PCI_IDE_RP]);
if (!rp_stream)
return NULL;
struct stream_index *ep_stream __free(free_stream) = alloc_stream_index(
&pdev->ide_stream_ida, pdev->nr_sel_ide, &__stream[PCI_IDE_EP]);
if (!ep_stream)
return NULL;
/* for SR-IOV case, cover all VFs */
num_vf = pci_num_vf(pdev);
if (num_vf)
rid_end = PCI_DEVID(pci_iov_virtfn_bus(pdev, num_vf),
pci_iov_virtfn_devfn(pdev, num_vf));
else
rid_end = pci_dev_id(pdev);
br = pci_upstream_bridge(pdev);
if (!br)
return NULL;
/*
* Check if the device consumes memory and/or prefetch-memory. Setup
* downstream address association ranges for each.
*/
mem = pci_resource_n(br, PCI_BRIDGE_MEM_WINDOW);
pref = pci_resource_n(br, PCI_BRIDGE_PREF_MEM_WINDOW);
if (resource_assigned(mem))
pcibios_resource_to_bus(br->bus, &mem_assoc, mem);
if (resource_assigned(pref))
pcibios_resource_to_bus(br->bus, &pref_assoc, pref);
*ide = (struct pci_ide) {
.pdev = pdev,
.partner = {
[PCI_IDE_EP] = {
.rid_start = pci_dev_id(rp),
.rid_end = pci_dev_id(rp),
.stream_index = no_free_ptr(ep_stream)->stream_index,
/* Disable upstream address association */
.mem_assoc = { 0, -1 },
.pref_assoc = { 0, -1 },
},
[PCI_IDE_RP] = {
.rid_start = pci_dev_id(pdev),
.rid_end = rid_end,
.stream_index = no_free_ptr(rp_stream)->stream_index,
.mem_assoc = mem_assoc,
.pref_assoc = pref_assoc,
},
},
.host_bridge_stream = no_free_ptr(hb_stream)->stream_index,
.stream_id = -1,
};
return_ptr(ide);
}
EXPORT_SYMBOL_GPL(pci_ide_stream_alloc);
/**
* pci_ide_stream_free() - unwind pci_ide_stream_alloc()
* @ide: idle IDE settings descriptor
*
* Free all of the stream index (register block) allocations acquired by
* pci_ide_stream_alloc(). The stream represented by @ide is assumed to
* be unregistered and not instantiated in any device.
*/
void pci_ide_stream_free(struct pci_ide *ide)
{
struct pci_dev *pdev = ide->pdev;
struct pci_dev *rp = pcie_find_root_port(pdev);
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
ida_free(&pdev->ide_stream_ida, ide->partner[PCI_IDE_EP].stream_index);
ida_free(&rp->ide_stream_ida, ide->partner[PCI_IDE_RP].stream_index);
ida_free(&hb->ide_stream_ida, ide->host_bridge_stream);
kfree(ide);
}
EXPORT_SYMBOL_GPL(pci_ide_stream_free);
/**
* pci_ide_stream_release() - unwind and release an @ide context
* @ide: partially or fully registered IDE settings descriptor
*
* In support of automatic cleanup of IDE setup routines perform IDE
* teardown in expected reverse order of setup and with respect to which
* aspects of IDE setup have successfully completed.
*
* Be careful that setup order mirrors this shutdown order. Otherwise,
* open code releasing the IDE context.
*/
void pci_ide_stream_release(struct pci_ide *ide)
{
struct pci_dev *pdev = ide->pdev;
struct pci_dev *rp = pcie_find_root_port(pdev);
if (ide->partner[PCI_IDE_RP].enable)
pci_ide_stream_disable(rp, ide);
if (ide->partner[PCI_IDE_EP].enable)
pci_ide_stream_disable(pdev, ide);
if (ide->tsm_dev)
tsm_ide_stream_unregister(ide);
if (ide->partner[PCI_IDE_RP].setup)
pci_ide_stream_teardown(rp, ide);
if (ide->partner[PCI_IDE_EP].setup)
pci_ide_stream_teardown(pdev, ide);
if (ide->name)
pci_ide_stream_unregister(ide);
pci_ide_stream_free(ide);
}
EXPORT_SYMBOL_GPL(pci_ide_stream_release);
struct pci_ide_stream_id {
struct pci_host_bridge *hb;
u8 stream_id;
};
static struct pci_ide_stream_id *
request_stream_id(struct pci_host_bridge *hb, u8 stream_id,
struct pci_ide_stream_id *sid)
{
if (!reserve_stream_id(hb, stream_id))
return NULL;
*sid = (struct pci_ide_stream_id) {
.hb = hb,
.stream_id = stream_id,
};
return sid;
}
DEFINE_FREE(free_stream_id, struct pci_ide_stream_id *,
if (_T) ida_free(&_T->hb->ide_stream_ids_ida, _T->stream_id))
/**
* pci_ide_stream_register() - Prepare to activate an IDE Stream
* @ide: IDE settings descriptor
*
* After a Stream ID has been acquired for @ide, record the presence of
* the stream in sysfs. The expectation is that @ide is immutable while
* registered.
*/
int pci_ide_stream_register(struct pci_ide *ide)
{
struct pci_dev *pdev = ide->pdev;
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
struct pci_ide_stream_id __sid;
u8 ep_stream, rp_stream;
int rc;
if (ide->stream_id < 0 || ide->stream_id > U8_MAX) {
pci_err(pdev, "Setup fail: Invalid Stream ID: %d\n", ide->stream_id);
return -ENXIO;
}
struct pci_ide_stream_id *sid __free(free_stream_id) =
request_stream_id(hb, ide->stream_id, &__sid);
if (!sid) {
pci_err(pdev, "Setup fail: Stream ID %d in use\n", ide->stream_id);
return -EBUSY;
}
ep_stream = ide->partner[PCI_IDE_EP].stream_index;
rp_stream = ide->partner[PCI_IDE_RP].stream_index;
const char *name __free(kfree) = kasprintf(GFP_KERNEL, "stream%d.%d.%d",
ide->host_bridge_stream,
rp_stream, ep_stream);
if (!name)
return -ENOMEM;
rc = sysfs_create_link(&hb->dev.kobj, &pdev->dev.kobj, name);
if (rc)
return rc;
ide->name = no_free_ptr(name);
/* Stream ID reservation recorded in @ide is now successfully registered */
retain_and_null_ptr(sid);
return 0;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_register);
/**
* pci_ide_stream_unregister() - unwind pci_ide_stream_register()
* @ide: idle IDE settings descriptor
*
* In preparation for freeing @ide, remove sysfs enumeration for the
* stream.
*/
void pci_ide_stream_unregister(struct pci_ide *ide)
{
struct pci_dev *pdev = ide->pdev;
struct pci_host_bridge *hb = pci_find_host_bridge(pdev->bus);
sysfs_remove_link(&hb->dev.kobj, ide->name);
kfree(ide->name);
ida_free(&hb->ide_stream_ids_ida, ide->stream_id);
ide->name = NULL;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_unregister);
static int pci_ide_domain(struct pci_dev *pdev)
{
if (pdev->fm_enabled)
return pci_domain_nr(pdev->bus);
return 0;
}
struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev, struct pci_ide *ide)
{
if (!pci_is_pcie(pdev)) {
pci_warn_once(pdev, "not a PCIe device\n");
return NULL;
}
switch (pci_pcie_type(pdev)) {
case PCI_EXP_TYPE_ENDPOINT:
if (pdev != ide->pdev) {
pci_warn_once(pdev, "setup expected Endpoint: %s\n", pci_name(ide->pdev));
return NULL;
}
return &ide->partner[PCI_IDE_EP];
case PCI_EXP_TYPE_ROOT_PORT: {
struct pci_dev *rp = pcie_find_root_port(ide->pdev);
if (pdev != rp) {
pci_warn_once(pdev, "setup expected Root Port: %s\n",
pci_name(rp));
return NULL;
}
return &ide->partner[PCI_IDE_RP];
}
default:
pci_warn_once(pdev, "invalid device type\n");
return NULL;
}
}
EXPORT_SYMBOL_GPL(pci_ide_to_settings);
static void set_ide_sel_ctl(struct pci_dev *pdev, struct pci_ide *ide,
struct pci_ide_partner *settings, int pos,
bool enable)
{
u32 val = FIELD_PREP(PCI_IDE_SEL_CTL_ID, ide->stream_id) |
FIELD_PREP(PCI_IDE_SEL_CTL_DEFAULT, settings->default_stream) |
FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, pdev->ide_cfg) |
FIELD_PREP(PCI_IDE_SEL_CTL_TEE_LIMITED, pdev->ide_tee_limit) |
FIELD_PREP(PCI_IDE_SEL_CTL_EN, enable);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
}
#define SEL_ADDR1_LOWER GENMASK(31, 20)
#define SEL_ADDR_UPPER GENMASK_ULL(63, 32)
#define PREP_PCI_IDE_SEL_ADDR1(base, limit) \
(FIELD_PREP(PCI_IDE_SEL_ADDR_1_VALID, 1) | \
FIELD_PREP(PCI_IDE_SEL_ADDR_1_BASE_LOW, \
FIELD_GET(SEL_ADDR1_LOWER, (base))) | \
FIELD_PREP(PCI_IDE_SEL_ADDR_1_LIMIT_LOW, \
FIELD_GET(SEL_ADDR1_LOWER, (limit))))
static void mem_assoc_to_regs(struct pci_bus_region *region,
struct pci_ide_regs *regs, int idx)
{
/* convert to u64 range for bitfield size checks */
struct range r = { region->start, region->end };
regs->addr[idx].assoc1 = PREP_PCI_IDE_SEL_ADDR1(r.start, r.end);
regs->addr[idx].assoc2 = FIELD_GET(SEL_ADDR_UPPER, r.end);
regs->addr[idx].assoc3 = FIELD_GET(SEL_ADDR_UPPER, r.start);
}
/**
* pci_ide_stream_to_regs() - convert IDE settings to association register values
* @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
* @ide: registered IDE settings descriptor
* @regs: output register values
*/
static void pci_ide_stream_to_regs(struct pci_dev *pdev, struct pci_ide *ide,
struct pci_ide_regs *regs)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
int assoc_idx = 0;
memset(regs, 0, sizeof(*regs));
if (!settings)
return;
regs->rid1 = FIELD_PREP(PCI_IDE_SEL_RID_1_LIMIT, settings->rid_end);
regs->rid2 = FIELD_PREP(PCI_IDE_SEL_RID_2_VALID, 1) |
FIELD_PREP(PCI_IDE_SEL_RID_2_BASE, settings->rid_start) |
FIELD_PREP(PCI_IDE_SEL_RID_2_SEG, pci_ide_domain(pdev));
if (pdev->nr_ide_mem && pci_bus_region_size(&settings->mem_assoc)) {
mem_assoc_to_regs(&settings->mem_assoc, regs, assoc_idx);
assoc_idx++;
}
if (pdev->nr_ide_mem > assoc_idx &&
pci_bus_region_size(&settings->pref_assoc)) {
mem_assoc_to_regs(&settings->pref_assoc, regs, assoc_idx);
assoc_idx++;
}
regs->nr_addr = assoc_idx;
}
/**
* pci_ide_stream_setup() - program settings to Selective IDE Stream registers
* @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
* @ide: registered IDE settings descriptor
*
* When @pdev is a PCI_EXP_TYPE_ENDPOINT then the PCI_IDE_EP partner
* settings are written to @pdev's Selective IDE Stream register block,
* and when @pdev is a PCI_EXP_TYPE_ROOT_PORT, the PCI_IDE_RP settings
* are selected.
*/
void pci_ide_stream_setup(struct pci_dev *pdev, struct pci_ide *ide)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
struct pci_ide_regs regs;
int pos;
if (!settings)
return;
pci_ide_stream_to_regs(pdev, ide, &regs);
pos = sel_ide_offset(pdev, settings);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, regs.rid1);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, regs.rid2);
for (int i = 0; i < regs.nr_addr; i++) {
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_1(i),
regs.addr[i].assoc1);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_2(i),
regs.addr[i].assoc2);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_3(i),
regs.addr[i].assoc3);
}
/* clear extra unused address association blocks */
for (int i = regs.nr_addr; i < pdev->nr_ide_mem; i++) {
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_1(i), 0);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_2(i), 0);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_3(i), 0);
}
/*
* Setup control register early for devices that expect
* stream_id is set during key programming.
*/
set_ide_sel_ctl(pdev, ide, settings, pos, false);
settings->setup = 1;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_setup);
/**
* pci_ide_stream_teardown() - disable the stream and clear all settings
* @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
* @ide: registered IDE settings descriptor
*
* For stream destruction, zero all registers that may have been written
* by pci_ide_stream_setup(). Consider pci_ide_stream_disable() to leave
* settings in place while temporarily disabling the stream.
*/
void pci_ide_stream_teardown(struct pci_dev *pdev, struct pci_ide *ide)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
int pos, i;
if (!settings)
return;
pos = sel_ide_offset(pdev, settings);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
for (i = 0; i < pdev->nr_ide_mem; i++) {
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_1(i), 0);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_2(i), 0);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_ADDR_3(i), 0);
}
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_2, 0);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_RID_1, 0);
settings->setup = 0;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_teardown);
/**
* pci_ide_stream_enable() - enable a Selective IDE Stream
* @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
* @ide: registered and setup IDE settings descriptor
*
* Activate the stream by writing to the Selective IDE Stream Control
* Register.
*
* Return: 0 if the stream successfully entered the "secure" state, and -EINVAL
* if @ide is invalid, and -ENXIO if the stream fails to enter the secure state.
*
* Note that the state may go "insecure" at any point after returning 0, but
* those events are equivalent to a "link down" event and handled via
* asynchronous error reporting.
*
* Caller is responsible to clear the enable bit in the -ENXIO case.
*/
int pci_ide_stream_enable(struct pci_dev *pdev, struct pci_ide *ide)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
int pos;
u32 val;
if (!settings)
return -EINVAL;
pos = sel_ide_offset(pdev, settings);
set_ide_sel_ctl(pdev, ide, settings, pos, true);
settings->enable = 1;
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_STS, &val);
if (FIELD_GET(PCI_IDE_SEL_STS_STATE, val) !=
PCI_IDE_SEL_STS_STATE_SECURE)
return -ENXIO;
return 0;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_enable);
/**
* pci_ide_stream_disable() - disable a Selective IDE Stream
* @pdev: PCIe device object for either a Root Port or Endpoint Partner Port
* @ide: registered and setup IDE settings descriptor
*
* Clear the Selective IDE Stream Control Register, but leave all other
* registers untouched.
*/
void pci_ide_stream_disable(struct pci_dev *pdev, struct pci_ide *ide)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
int pos;
if (!settings)
return;
pos = sel_ide_offset(pdev, settings);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, 0);
settings->enable = 0;
}
EXPORT_SYMBOL_GPL(pci_ide_stream_disable);
void pci_ide_init_host_bridge(struct pci_host_bridge *hb)
{
hb->nr_ide_streams = 256;
ida_init(&hb->ide_stream_ida);
ida_init(&hb->ide_stream_ids_ida);
reserve_stream_id(hb, PCI_IDE_RESERVED_STREAM_ID);
}
static ssize_t available_secure_streams_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct pci_host_bridge *hb = to_pci_host_bridge(dev);
int nr = READ_ONCE(hb->nr_ide_streams);
int avail = nr;
if (!nr)
return -ENXIO;
/*
* Yes, this is inefficient and racy, but it is only for occasional
* platform resource surveys. Worst case is bounded to 256 streams.
*/
for (int i = 0; i < nr; i++)
if (ida_exists(&hb->ide_stream_ida, i))
avail--;
return sysfs_emit(buf, "%d\n", avail);
}
static DEVICE_ATTR_RO(available_secure_streams);
static struct attribute *pci_ide_attrs[] = {
&dev_attr_available_secure_streams.attr,
NULL
};
static umode_t pci_ide_attr_visible(struct kobject *kobj, struct attribute *a, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct pci_host_bridge *hb = to_pci_host_bridge(dev);
if (a == &dev_attr_available_secure_streams.attr)
if (!hb->nr_ide_streams)
return 0;
return a->mode;
}
const struct attribute_group pci_ide_attr_group = {
.attrs = pci_ide_attrs,
.is_visible = pci_ide_attr_visible,
};
/**
* pci_ide_set_nr_streams() - sets size of the pool of IDE Stream resources
* @hb: host bridge boundary for the stream pool
* @nr: number of streams
*
* Platform PCI init and/or expert test module use only. Limit IDE
* Stream establishment by setting the number of stream resources
* available at the host bridge. Platform init code must set this before
* the first pci_ide_stream_alloc() call if the platform has less than the
* default of 256 streams per host-bridge.
*
* The "PCI_IDE" symbol namespace is required because this is typically
* a detail that is settled in early PCI init. I.e. this export is not
* for endpoint drivers.
*/
void pci_ide_set_nr_streams(struct pci_host_bridge *hb, u16 nr)
{
hb->nr_ide_streams = min(nr, 256);
WARN_ON_ONCE(!ida_is_empty(&hb->ide_stream_ida));
sysfs_update_group(&hb->dev.kobj, &pci_ide_attr_group);
}
EXPORT_SYMBOL_NS_GPL(pci_ide_set_nr_streams, "PCI_IDE");
void pci_ide_destroy(struct pci_dev *pdev)
{
ida_destroy(&pdev->ide_stream_ida);
}

View File

@@ -1855,6 +1855,10 @@ const struct attribute_group *pci_dev_attr_groups[] = {
#endif
#ifdef CONFIG_PCI_DOE
&pci_doe_sysfs_group,
#endif
#ifdef CONFIG_PCI_TSM
&pci_tsm_auth_attr_group,
&pci_tsm_attr_group,
#endif
NULL,
};

View File

@@ -615,6 +615,27 @@ static inline void pci_doe_sysfs_init(struct pci_dev *pdev) { }
static inline void pci_doe_sysfs_teardown(struct pci_dev *pdev) { }
#endif
#ifdef CONFIG_PCI_IDE
void pci_ide_init(struct pci_dev *dev);
void pci_ide_init_host_bridge(struct pci_host_bridge *hb);
void pci_ide_destroy(struct pci_dev *dev);
extern const struct attribute_group pci_ide_attr_group;
#else
static inline void pci_ide_init(struct pci_dev *dev) { }
static inline void pci_ide_init_host_bridge(struct pci_host_bridge *hb) { }
static inline void pci_ide_destroy(struct pci_dev *dev) { }
#endif
#ifdef CONFIG_PCI_TSM
void pci_tsm_init(struct pci_dev *pdev);
void pci_tsm_destroy(struct pci_dev *pdev);
extern const struct attribute_group pci_tsm_attr_group;
extern const struct attribute_group pci_tsm_auth_attr_group;
#else
static inline void pci_tsm_init(struct pci_dev *pdev) { }
static inline void pci_tsm_destroy(struct pci_dev *pdev) { }
#endif
/**
* pci_dev_set_io_state - Set the new error state if possible.
*

View File

@@ -658,6 +658,18 @@ static void pci_release_host_bridge_dev(struct device *dev)
kfree(bridge);
}
static const struct attribute_group *pci_host_bridge_groups[] = {
#ifdef CONFIG_PCI_IDE
&pci_ide_attr_group,
#endif
NULL
};
static const struct device_type pci_host_bridge_type = {
.groups = pci_host_bridge_groups,
.release = pci_release_host_bridge_dev,
};
static void pci_init_host_bridge(struct pci_host_bridge *bridge)
{
INIT_LIST_HEAD(&bridge->windows);
@@ -677,6 +689,8 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
bridge->native_dpc = 1;
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
bridge->native_cxl_error = 1;
bridge->dev.type = &pci_host_bridge_type;
pci_ide_init_host_bridge(bridge);
device_initialize(&bridge->dev);
}
@@ -690,7 +704,6 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
return NULL;
pci_init_host_bridge(bridge);
bridge->dev.release = pci_release_host_bridge_dev;
return bridge;
}
@@ -2296,6 +2309,17 @@ int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
return 0;
}
static void pci_dev3_init(struct pci_dev *pdev)
{
u16 cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DEV3);
u32 val = 0;
if (!cap)
return;
pci_read_config_dword(pdev, cap + PCI_DEV3_STA, &val);
pdev->fm_enabled = !!(val & PCI_DEV3_STA_SEGMENT);
}
/**
* pcie_relaxed_ordering_enabled - Probe for PCIe relaxed ordering enable
* @dev: PCI device to query
@@ -2680,6 +2704,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_doe_init(dev); /* Data Object Exchange */
pci_tph_init(dev); /* TLP Processing Hints */
pci_rebar_init(dev); /* Resizable BAR */
pci_dev3_init(dev); /* Device 3 capabilities */
pci_ide_init(dev); /* Link Integrity and Data Encryption */
pcie_report_downtraining(dev);
pci_init_reset_methods(dev);
@@ -2773,6 +2799,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
ret = device_add(&dev->dev);
WARN_ON(ret < 0);
/* Establish pdev->tsm for newly added (e.g. new SR-IOV VFs) */
pci_tsm_init(dev);
pci_npem_create(dev);
pci_doe_sysfs_init(dev);

View File

@@ -57,6 +57,12 @@ static void pci_destroy_dev(struct pci_dev *dev)
pci_doe_sysfs_teardown(dev);
pci_npem_remove(dev);
/*
* While device is in D0 drop the device from TSM link operations
* including unbind and disconnect (IDE + SPDM teardown).
*/
pci_tsm_destroy(dev);
device_del(&dev->dev);
down_write(&pci_bus_sem);
@@ -64,6 +70,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
up_write(&pci_bus_sem);
pci_doe_destroy(dev);
pci_ide_destroy(dev);
pcie_aspm_exit_link_state(dev);
pci_bridge_d3_update(dev);
pci_pwrctrl_unregister(&dev->dev);

View File

@@ -282,6 +282,45 @@ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
return pdev;
}
static struct pci_dev *pci_get_dev_by_id_reverse(const struct pci_device_id *id,
struct pci_dev *from)
{
struct device *dev;
struct device *dev_start = NULL;
struct pci_dev *pdev = NULL;
if (from)
dev_start = &from->dev;
dev = bus_find_device_reverse(&pci_bus_type, dev_start, (void *)id,
match_pci_dev_by_id);
if (dev)
pdev = to_pci_dev(dev);
pci_dev_put(from);
return pdev;
}
enum pci_search_direction {
PCI_SEARCH_FORWARD,
PCI_SEARCH_REVERSE,
};
static struct pci_dev *__pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
struct pci_dev *from, enum pci_search_direction dir)
{
struct pci_device_id id = {
.vendor = vendor,
.device = device,
.subvendor = ss_vendor,
.subdevice = ss_device,
};
if (dir == PCI_SEARCH_FORWARD)
return pci_get_dev_by_id(&id, from);
else
return pci_get_dev_by_id_reverse(&id, from);
}
/**
* pci_get_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
@@ -302,14 +341,8 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
struct pci_dev *from)
{
struct pci_device_id id = {
.vendor = vendor,
.device = device,
.subvendor = ss_vendor,
.subdevice = ss_device,
};
return pci_get_dev_by_id(&id, from);
return __pci_get_subsys(vendor, device, ss_vendor, ss_device, from,
PCI_SEARCH_FORWARD);
}
EXPORT_SYMBOL(pci_get_subsys);
@@ -334,6 +367,19 @@ struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
}
EXPORT_SYMBOL(pci_get_device);
/*
* Same semantics as pci_get_device(), except walks the PCI device list
* in reverse discovery order.
*/
struct pci_dev *pci_get_device_reverse(unsigned int vendor,
unsigned int device,
struct pci_dev *from)
{
return __pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from,
PCI_SEARCH_REVERSE);
}
EXPORT_SYMBOL(pci_get_device_reverse);
/**
* pci_get_class - begin or continue searching for a PCI device by class
* @class: search for a PCI device with this class designation

900
drivers/pci/tsm.c Normal file
View File

@@ -0,0 +1,900 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interface with platform TEE Security Manager (TSM) objects as defined by
* PCIe r7.0 section 11 TEE Device Interface Security Protocol (TDISP)
*
* Copyright(c) 2024-2025 Intel Corporation. All rights reserved.
*/
#define dev_fmt(fmt) "PCI/TSM: " fmt
#include <linux/bitfield.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
#include <linux/pci-tsm.h>
#include <linux/sysfs.h>
#include <linux/tsm.h>
#include <linux/xarray.h>
#include "pci.h"
/*
* Provide a read/write lock against the init / exit of pdev tsm
* capabilities and arrival/departure of a TSM instance
*/
static DECLARE_RWSEM(pci_tsm_rwsem);
/*
* Count of TSMs registered that support physical link operations vs device
* security state management.
*/
static int pci_tsm_link_count;
static int pci_tsm_devsec_count;
static const struct pci_tsm_ops *to_pci_tsm_ops(struct pci_tsm *tsm)
{
return tsm->tsm_dev->pci_ops;
}
static inline bool is_dsm(struct pci_dev *pdev)
{
return pdev->tsm && pdev->tsm->dsm_dev == pdev;
}
static inline bool has_tee(struct pci_dev *pdev)
{
return pdev->devcap & PCI_EXP_DEVCAP_TEE;
}
/* 'struct pci_tsm_pf0' wraps 'struct pci_tsm' when ->dsm_dev == ->pdev (self) */
static struct pci_tsm_pf0 *to_pci_tsm_pf0(struct pci_tsm *tsm)
{
/*
* All "link" TSM contexts reference the device that hosts the DSM
* interface for a set of devices. Walk to the DSM device and cast its
* ->tsm context to a 'struct pci_tsm_pf0 *'.
*/
struct pci_dev *pf0 = tsm->dsm_dev;
if (!is_pci_tsm_pf0(pf0) || !is_dsm(pf0)) {
pci_WARN_ONCE(tsm->pdev, 1, "invalid context object\n");
return NULL;
}
return container_of(pf0->tsm, struct pci_tsm_pf0, base_tsm);
}
static void tsm_remove(struct pci_tsm *tsm)
{
struct pci_dev *pdev;
if (!tsm)
return;
pdev = tsm->pdev;
to_pci_tsm_ops(tsm)->remove(tsm);
pdev->tsm = NULL;
}
DEFINE_FREE(tsm_remove, struct pci_tsm *, if (_T) tsm_remove(_T))
static void pci_tsm_walk_fns(struct pci_dev *pdev,
int (*cb)(struct pci_dev *pdev, void *data),
void *data)
{
/* Walk subordinate physical functions */
for (int i = 0; i < 8; i++) {
struct pci_dev *pf __free(pci_dev_put) = pci_get_slot(
pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i));
if (!pf)
continue;
/* on entry function 0 has already run @cb */
if (i > 0)
cb(pf, data);
/* walk virtual functions of each pf */
for (int j = 0; j < pci_num_vf(pf); j++) {
struct pci_dev *vf __free(pci_dev_put) =
pci_get_domain_bus_and_slot(
pci_domain_nr(pf->bus),
pci_iov_virtfn_bus(pf, j),
pci_iov_virtfn_devfn(pf, j));
if (!vf)
continue;
cb(vf, data);
}
}
/*
* Walk downstream devices, assumes that an upstream DSM is
* limited to downstream physical functions
*/
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev))
pci_walk_bus(pdev->subordinate, cb, data);
}
static void pci_tsm_walk_fns_reverse(struct pci_dev *pdev,
int (*cb)(struct pci_dev *pdev,
void *data),
void *data)
{
/* Reverse walk downstream devices */
if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM && is_dsm(pdev))
pci_walk_bus_reverse(pdev->subordinate, cb, data);
/* Reverse walk subordinate physical functions */
for (int i = 7; i >= 0; i--) {
struct pci_dev *pf __free(pci_dev_put) = pci_get_slot(
pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), i));
if (!pf)
continue;
/* reverse walk virtual functions */
for (int j = pci_num_vf(pf) - 1; j >= 0; j--) {
struct pci_dev *vf __free(pci_dev_put) =
pci_get_domain_bus_and_slot(
pci_domain_nr(pf->bus),
pci_iov_virtfn_bus(pf, j),
pci_iov_virtfn_devfn(pf, j));
if (!vf)
continue;
cb(vf, data);
}
/* on exit, caller will run @cb on function 0 */
if (i > 0)
cb(pf, data);
}
}
static void link_sysfs_disable(struct pci_dev *pdev)
{
sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
}
static void link_sysfs_enable(struct pci_dev *pdev)
{
bool tee = has_tee(pdev);
pci_dbg(pdev, "%s Security Manager detected (%s%s%s)\n",
pdev->tsm ? "Device" : "Platform TEE",
pdev->ide_cap ? "IDE" : "", pdev->ide_cap && tee ? " " : "",
tee ? "TEE" : "");
sysfs_update_group(&pdev->dev.kobj, &pci_tsm_auth_attr_group);
sysfs_update_group(&pdev->dev.kobj, &pci_tsm_attr_group);
}
static int probe_fn(struct pci_dev *pdev, void *dsm)
{
struct pci_dev *dsm_dev = dsm;
const struct pci_tsm_ops *ops = to_pci_tsm_ops(dsm_dev->tsm);
pdev->tsm = ops->probe(dsm_dev->tsm->tsm_dev, pdev);
pci_dbg(pdev, "setup TSM context: DSM: %s status: %s\n",
pci_name(dsm_dev), pdev->tsm ? "success" : "failed");
if (pdev->tsm)
link_sysfs_enable(pdev);
return 0;
}
static int pci_tsm_connect(struct pci_dev *pdev, struct tsm_dev *tsm_dev)
{
int rc;
struct pci_tsm_pf0 *tsm_pf0;
const struct pci_tsm_ops *ops = tsm_dev->pci_ops;
struct pci_tsm *pci_tsm __free(tsm_remove) = ops->probe(tsm_dev, pdev);
/* connect() mutually exclusive with subfunction pci_tsm_init() */
lockdep_assert_held_write(&pci_tsm_rwsem);
if (!pci_tsm)
return -ENXIO;
pdev->tsm = pci_tsm;
tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
/* mutex_intr assumes connect() is always sysfs/user driven */
ACQUIRE(mutex_intr, lock)(&tsm_pf0->lock);
if ((rc = ACQUIRE_ERR(mutex_intr, &lock)))
return rc;
rc = ops->connect(pdev);
if (rc)
return rc;
pdev->tsm = no_free_ptr(pci_tsm);
/*
* Now that the DSM is established, probe() all the potential
* dependent functions. Failure to probe a function is not fatal
* to connect(), it just disables subsequent security operations
* for that function.
*
* Note this is done unconditionally, without regard to finding
* PCI_EXP_DEVCAP_TEE on the dependent function, for robustness. The DSM
* is the ultimate arbiter of security state relative to a given
* interface id, and if it says it can manage TDISP state of a function,
* let it.
*/
if (has_tee(pdev))
pci_tsm_walk_fns(pdev, probe_fn, pdev);
return 0;
}
static ssize_t connect_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct tsm_dev *tsm_dev;
int rc;
ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
return rc;
if (!pdev->tsm)
return sysfs_emit(buf, "\n");
tsm_dev = pdev->tsm->tsm_dev;
return sysfs_emit(buf, "%s\n", dev_name(&tsm_dev->dev));
}
/* Is @tsm_dev managing physical link / session properties... */
static bool is_link_tsm(struct tsm_dev *tsm_dev)
{
return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->link_ops.probe;
}
/* ...or is @tsm_dev managing device security state ? */
static bool is_devsec_tsm(struct tsm_dev *tsm_dev)
{
return tsm_dev && tsm_dev->pci_ops && tsm_dev->pci_ops->devsec_ops.lock;
}
static ssize_t connect_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
struct pci_dev *pdev = to_pci_dev(dev);
int rc, id;
rc = sscanf(buf, "tsm%d\n", &id);
if (rc != 1)
return -EINVAL;
ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock)))
return rc;
if (pdev->tsm)
return -EBUSY;
struct tsm_dev *tsm_dev __free(put_tsm_dev) = find_tsm_dev(id);
if (!is_link_tsm(tsm_dev))
return -ENXIO;
rc = pci_tsm_connect(pdev, tsm_dev);
if (rc)
return rc;
return len;
}
static DEVICE_ATTR_RW(connect);
static int remove_fn(struct pci_dev *pdev, void *data)
{
tsm_remove(pdev->tsm);
link_sysfs_disable(pdev);
return 0;
}
/*
* Note, this helper only returns an error code and takes an argument for
* compatibility with the pci_walk_bus() callback prototype. pci_tsm_unbind()
* always succeeds.
*/
static int __pci_tsm_unbind(struct pci_dev *pdev, void *data)
{
struct pci_tdi *tdi;
struct pci_tsm_pf0 *tsm_pf0;
lockdep_assert_held(&pci_tsm_rwsem);
if (!pdev->tsm)
return 0;
tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
guard(mutex)(&tsm_pf0->lock);
tdi = pdev->tsm->tdi;
if (!tdi)
return 0;
to_pci_tsm_ops(pdev->tsm)->unbind(tdi);
pdev->tsm->tdi = NULL;
return 0;
}
void pci_tsm_unbind(struct pci_dev *pdev)
{
guard(rwsem_read)(&pci_tsm_rwsem);
__pci_tsm_unbind(pdev, NULL);
}
EXPORT_SYMBOL_GPL(pci_tsm_unbind);
/**
* pci_tsm_bind() - Bind @pdev as a TDI for @kvm
* @pdev: PCI device function to bind
* @kvm: Private memory attach context
* @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
*
* Returns 0 on success, or a negative error code on failure.
*
* Context: Caller is responsible for constraining the bind lifetime to the
* registered state of the device. For example, pci_tsm_bind() /
* pci_tsm_unbind() limited to the VFIO driver bound state of the device.
*/
int pci_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id)
{
struct pci_tsm_pf0 *tsm_pf0;
struct pci_tdi *tdi;
if (!kvm)
return -EINVAL;
guard(rwsem_read)(&pci_tsm_rwsem);
if (!pdev->tsm)
return -EINVAL;
if (!is_link_tsm(pdev->tsm->tsm_dev))
return -ENXIO;
tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
guard(mutex)(&tsm_pf0->lock);
/* Resolve races to bind a TDI */
if (pdev->tsm->tdi) {
if (pdev->tsm->tdi->kvm != kvm)
return -EBUSY;
return 0;
}
tdi = to_pci_tsm_ops(pdev->tsm)->bind(pdev, kvm, tdi_id);
if (IS_ERR(tdi))
return PTR_ERR(tdi);
pdev->tsm->tdi = tdi;
return 0;
}
EXPORT_SYMBOL_GPL(pci_tsm_bind);
/**
* pci_tsm_guest_req() - helper to marshal guest requests to the TSM driver
* @pdev: @pdev representing a bound tdi
* @scope: caller asserts this passthrough request is limited to TDISP operations
* @req_in: Input payload forwarded from the guest
* @in_len: Length of @req_in
* @req_out: Output payload buffer response to the guest
* @out_len: Length of @req_out on input, bytes filled in @req_out on output
* @tsm_code: Optional TSM arch specific result code for the guest TSM
*
* This is a common entry point for requests triggered by userspace KVM-exit
* service handlers responding to TDI information or state change requests. The
* scope parameter limits requests to TDISP state management, or limited debug.
* This path is only suitable for commands and results that are the host kernel
* has no use, the host is only facilitating guest to TSM communication.
*
* Returns 0 on success and -error on failure and positive "residue" on success
* but @req_out is filled with less then @out_len, or @req_out is NULL and a
* residue number of bytes were not consumed from @req_in. On success or
* failure @tsm_code may be populated with a TSM implementation specific result
* code for the guest to consume.
*
* Context: Caller is responsible for calling this within the pci_tsm_bind()
* state of the TDI.
*/
ssize_t pci_tsm_guest_req(struct pci_dev *pdev, enum pci_tsm_req_scope scope,
sockptr_t req_in, size_t in_len, sockptr_t req_out,
size_t out_len, u64 *tsm_code)
{
struct pci_tsm_pf0 *tsm_pf0;
struct pci_tdi *tdi;
int rc;
/* Forbid requests that are not directly related to TDISP operations */
if (scope > PCI_TSM_REQ_STATE_CHANGE)
return -EINVAL;
ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
return rc;
if (!pdev->tsm)
return -ENXIO;
if (!is_link_tsm(pdev->tsm->tsm_dev))
return -ENXIO;
tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock);
if ((rc = ACQUIRE_ERR(mutex_intr, &ops_lock)))
return rc;
tdi = pdev->tsm->tdi;
if (!tdi)
return -ENXIO;
return to_pci_tsm_ops(pdev->tsm)->guest_req(tdi, scope, req_in, in_len,
req_out, out_len, tsm_code);
}
EXPORT_SYMBOL_GPL(pci_tsm_guest_req);
static void pci_tsm_unbind_all(struct pci_dev *pdev)
{
pci_tsm_walk_fns_reverse(pdev, __pci_tsm_unbind, NULL);
__pci_tsm_unbind(pdev, NULL);
}
static void __pci_tsm_disconnect(struct pci_dev *pdev)
{
struct pci_tsm_pf0 *tsm_pf0 = to_pci_tsm_pf0(pdev->tsm);
const struct pci_tsm_ops *ops = to_pci_tsm_ops(pdev->tsm);
/* disconnect() mutually exclusive with subfunction pci_tsm_init() */
lockdep_assert_held_write(&pci_tsm_rwsem);
pci_tsm_unbind_all(pdev);
/*
* disconnect() is uninterruptible as it may be called for device
* teardown
*/
guard(mutex)(&tsm_pf0->lock);
pci_tsm_walk_fns_reverse(pdev, remove_fn, NULL);
ops->disconnect(pdev);
}
static void pci_tsm_disconnect(struct pci_dev *pdev)
{
__pci_tsm_disconnect(pdev);
tsm_remove(pdev->tsm);
}
static ssize_t disconnect_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t len)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct tsm_dev *tsm_dev;
int rc;
ACQUIRE(rwsem_write_kill, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &lock)))
return rc;
if (!pdev->tsm)
return -ENXIO;
tsm_dev = pdev->tsm->tsm_dev;
if (!sysfs_streq(buf, dev_name(&tsm_dev->dev)))
return -EINVAL;
pci_tsm_disconnect(pdev);
return len;
}
static DEVICE_ATTR_WO(disconnect);
static ssize_t bound_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_tsm_pf0 *tsm_pf0;
struct pci_tsm *tsm;
int rc;
ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
return rc;
tsm = pdev->tsm;
if (!tsm)
return sysfs_emit(buf, "\n");
tsm_pf0 = to_pci_tsm_pf0(tsm);
ACQUIRE(mutex_intr, ops_lock)(&tsm_pf0->lock);
if ((rc = ACQUIRE_ERR(mutex_intr, &ops_lock)))
return rc;
if (!tsm->tdi)
return sysfs_emit(buf, "\n");
return sysfs_emit(buf, "%s\n", dev_name(&tsm->tsm_dev->dev));
}
static DEVICE_ATTR_RO(bound);
static ssize_t dsm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_tsm *tsm;
int rc;
ACQUIRE(rwsem_read_intr, lock)(&pci_tsm_rwsem);
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &lock)))
return rc;
tsm = pdev->tsm;
if (!tsm)
return sysfs_emit(buf, "\n");
return sysfs_emit(buf, "%s\n", pci_name(tsm->dsm_dev));
}
static DEVICE_ATTR_RO(dsm);
/* The 'authenticated' attribute is exclusive to the presence of a 'link' TSM */
static bool pci_tsm_link_group_visible(struct kobject *kobj)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
if (!pci_tsm_link_count)
return false;
if (!pci_is_pcie(pdev))
return false;
if (is_pci_tsm_pf0(pdev))
return true;
/*
* Show 'authenticated' and other attributes for the managed
* sub-functions of a DSM.
*/
if (pdev->tsm)
return true;
return false;
}
DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(pci_tsm_link);
/*
* 'link' and 'devsec' TSMs share the same 'tsm/' sysfs group, so the TSM type
* specific attributes need individual visibility checks.
*/
static umode_t pci_tsm_attr_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
if (pci_tsm_link_group_visible(kobj)) {
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
if (attr == &dev_attr_bound.attr) {
if (is_pci_tsm_pf0(pdev) && has_tee(pdev))
return attr->mode;
if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
return attr->mode;
}
if (attr == &dev_attr_dsm.attr) {
if (is_pci_tsm_pf0(pdev))
return attr->mode;
if (pdev->tsm && has_tee(pdev->tsm->dsm_dev))
return attr->mode;
}
if (attr == &dev_attr_connect.attr ||
attr == &dev_attr_disconnect.attr) {
if (is_pci_tsm_pf0(pdev))
return attr->mode;
}
}
return 0;
}
static bool pci_tsm_group_visible(struct kobject *kobj)
{
return pci_tsm_link_group_visible(kobj);
}
DEFINE_SYSFS_GROUP_VISIBLE(pci_tsm);
static struct attribute *pci_tsm_attrs[] = {
&dev_attr_connect.attr,
&dev_attr_disconnect.attr,
&dev_attr_bound.attr,
&dev_attr_dsm.attr,
NULL
};
const struct attribute_group pci_tsm_attr_group = {
.name = "tsm",
.attrs = pci_tsm_attrs,
.is_visible = SYSFS_GROUP_VISIBLE(pci_tsm),
};
static ssize_t authenticated_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/*
* When the SPDM session established via TSM the 'authenticated' state
* of the device is identical to the connect state.
*/
return connect_show(dev, attr, buf);
}
static DEVICE_ATTR_RO(authenticated);
static struct attribute *pci_tsm_auth_attrs[] = {
&dev_attr_authenticated.attr,
NULL
};
const struct attribute_group pci_tsm_auth_attr_group = {
.attrs = pci_tsm_auth_attrs,
.is_visible = SYSFS_GROUP_VISIBLE(pci_tsm_link),
};
/*
* Retrieve physical function0 device whether it has TEE capability or not
*/
static struct pci_dev *pf0_dev_get(struct pci_dev *pdev)
{
struct pci_dev *pf_dev = pci_physfn(pdev);
if (PCI_FUNC(pf_dev->devfn) == 0)
return pci_dev_get(pf_dev);
return pci_get_slot(pf_dev->bus,
pf_dev->devfn - PCI_FUNC(pf_dev->devfn));
}
/*
* Find the PCI Device instance that serves as the Device Security Manager (DSM)
* for @pdev. Note that no additional reference is held for the resulting device
* because that resulting object always has a registered lifetime
* greater-than-or-equal to that of the @pdev argument. This is by virtue of
* @pdev being a descendant of, or identical to, the returned DSM device.
*/
static struct pci_dev *find_dsm_dev(struct pci_dev *pdev)
{
struct device *grandparent;
struct pci_dev *uport;
if (is_pci_tsm_pf0(pdev))
return pdev;
struct pci_dev *pf0 __free(pci_dev_put) = pf0_dev_get(pdev);
if (!pf0)
return NULL;
if (is_dsm(pf0))
return pf0;
/*
* For cases where a switch may be hosting TDISP services on behalf of
* downstream devices, check the first upstream port relative to this
* endpoint.
*/
if (!pdev->dev.parent)
return NULL;
grandparent = pdev->dev.parent->parent;
if (!grandparent)
return NULL;
if (!dev_is_pci(grandparent))
return NULL;
uport = to_pci_dev(grandparent);
if (!pci_is_pcie(uport) ||
pci_pcie_type(uport) != PCI_EXP_TYPE_UPSTREAM)
return NULL;
if (is_dsm(uport))
return uport;
return NULL;
}
/**
* pci_tsm_tdi_constructor() - base 'struct pci_tdi' initialization for link TSMs
* @pdev: PCI device function representing the TDI
* @tdi: context to initialize
* @kvm: Private memory attach context
* @tdi_id: Identifier (virtual BDF) for the TDI as referenced by the TSM and DSM
*/
void pci_tsm_tdi_constructor(struct pci_dev *pdev, struct pci_tdi *tdi,
struct kvm *kvm, u32 tdi_id)
{
tdi->pdev = pdev;
tdi->kvm = kvm;
tdi->tdi_id = tdi_id;
}
EXPORT_SYMBOL_GPL(pci_tsm_tdi_constructor);
/**
* pci_tsm_link_constructor() - base 'struct pci_tsm' initialization for link TSMs
* @pdev: The PCI device
* @tsm: context to initialize
* @tsm_dev: Platform TEE Security Manager, initiator of security operations
*/
int pci_tsm_link_constructor(struct pci_dev *pdev, struct pci_tsm *tsm,
struct tsm_dev *tsm_dev)
{
if (!is_link_tsm(tsm_dev))
return -EINVAL;
tsm->dsm_dev = find_dsm_dev(pdev);
if (!tsm->dsm_dev) {
pci_warn(pdev, "failed to find Device Security Manager\n");
return -ENXIO;
}
tsm->pdev = pdev;
tsm->tsm_dev = tsm_dev;
return 0;
}
EXPORT_SYMBOL_GPL(pci_tsm_link_constructor);
/**
* pci_tsm_pf0_constructor() - common 'struct pci_tsm_pf0' (DSM) initialization
* @pdev: Physical Function 0 PCI device (as indicated by is_pci_tsm_pf0())
* @tsm: context to initialize
* @tsm_dev: Platform TEE Security Manager, initiator of security operations
*/
int pci_tsm_pf0_constructor(struct pci_dev *pdev, struct pci_tsm_pf0 *tsm,
struct tsm_dev *tsm_dev)
{
mutex_init(&tsm->lock);
tsm->doe_mb = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
PCI_DOE_FEATURE_CMA);
if (!tsm->doe_mb) {
pci_warn(pdev, "TSM init failure, no CMA mailbox\n");
return -ENODEV;
}
return pci_tsm_link_constructor(pdev, &tsm->base_tsm, tsm_dev);
}
EXPORT_SYMBOL_GPL(pci_tsm_pf0_constructor);
void pci_tsm_pf0_destructor(struct pci_tsm_pf0 *pf0_tsm)
{
mutex_destroy(&pf0_tsm->lock);
}
EXPORT_SYMBOL_GPL(pci_tsm_pf0_destructor);
int pci_tsm_register(struct tsm_dev *tsm_dev)
{
struct pci_dev *pdev = NULL;
if (!tsm_dev)
return -EINVAL;
/* The TSM device must only implement one of link_ops or devsec_ops */
if (!is_link_tsm(tsm_dev) && !is_devsec_tsm(tsm_dev))
return -EINVAL;
if (is_link_tsm(tsm_dev) && is_devsec_tsm(tsm_dev))
return -EINVAL;
guard(rwsem_write)(&pci_tsm_rwsem);
/* On first enable, update sysfs groups */
if (is_link_tsm(tsm_dev) && pci_tsm_link_count++ == 0) {
for_each_pci_dev(pdev)
if (is_pci_tsm_pf0(pdev))
link_sysfs_enable(pdev);
} else if (is_devsec_tsm(tsm_dev)) {
pci_tsm_devsec_count++;
}
return 0;
}
static void pci_tsm_fn_exit(struct pci_dev *pdev)
{
__pci_tsm_unbind(pdev, NULL);
tsm_remove(pdev->tsm);
}
/**
* __pci_tsm_destroy() - destroy the TSM context for @pdev
* @pdev: device to cleanup
* @tsm_dev: the TSM device being removed, or NULL if @pdev is being removed.
*
* At device removal or TSM unregistration all established context
* with the TSM is torn down. Additionally, if there are no more TSMs
* registered, the PCI tsm/ sysfs attributes are hidden.
*/
static void __pci_tsm_destroy(struct pci_dev *pdev, struct tsm_dev *tsm_dev)
{
struct pci_tsm *tsm = pdev->tsm;
lockdep_assert_held_write(&pci_tsm_rwsem);
/*
* First, handle the TSM removal case to shutdown @pdev sysfs, this is
* skipped if the device itself is being removed since sysfs goes away
* naturally at that point
*/
if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev) && !pci_tsm_link_count)
link_sysfs_disable(pdev);
/* Nothing else to do if this device never attached to the departing TSM */
if (!tsm)
return;
/* Now lookup the tsm_dev to destroy TSM context */
if (!tsm_dev)
tsm_dev = tsm->tsm_dev;
else if (tsm_dev != tsm->tsm_dev)
return;
if (is_link_tsm(tsm_dev) && is_pci_tsm_pf0(pdev))
pci_tsm_disconnect(pdev);
else
pci_tsm_fn_exit(pdev);
}
void pci_tsm_destroy(struct pci_dev *pdev)
{
guard(rwsem_write)(&pci_tsm_rwsem);
__pci_tsm_destroy(pdev, NULL);
}
void pci_tsm_init(struct pci_dev *pdev)
{
guard(rwsem_read)(&pci_tsm_rwsem);
/*
* Subfunctions are either probed synchronous with connect() or later
* when either the SR-IOV configuration is changed, or, unlikely,
* connect() raced initial bus scanning.
*/
if (pdev->tsm)
return;
if (pci_tsm_link_count) {
struct pci_dev *dsm = find_dsm_dev(pdev);
if (!dsm)
return;
/*
* The only path to init a Device Security Manager capable
* device is via connect().
*/
if (!dsm->tsm)
return;
probe_fn(pdev, dsm);
}
}
void pci_tsm_unregister(struct tsm_dev *tsm_dev)
{
struct pci_dev *pdev = NULL;
guard(rwsem_write)(&pci_tsm_rwsem);
if (is_link_tsm(tsm_dev))
pci_tsm_link_count--;
if (is_devsec_tsm(tsm_dev))
pci_tsm_devsec_count--;
for_each_pci_dev_reverse(pdev)
__pci_tsm_destroy(pdev, tsm_dev);
}
int pci_tsm_doe_transfer(struct pci_dev *pdev, u8 type, const void *req,
size_t req_sz, void *resp, size_t resp_sz)
{
struct pci_tsm_pf0 *tsm;
if (!pdev->tsm || !is_pci_tsm_pf0(pdev))
return -ENXIO;
tsm = to_pci_tsm_pf0(pdev->tsm);
if (!tsm->doe_mb)
return -ENXIO;
return pci_doe(tsm->doe_mb, PCI_VENDOR_ID_PCI_SIG, type, req, req_sz,
resp, resp_sz);
}
EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer);

View File

@@ -81,10 +81,6 @@
#define MVOLT_1800 0
#define MVOLT_3300 1
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
static const char * const gpio_group_name[] = {
"gpioa", "gpiob", "gpioc", "gpiod", "gpioe", "gpiof", "gpiog",
"gpioh", "gpioi", "gpioj", "gpiok", "gpiol", "gpiom", "gpion",

View File

@@ -261,56 +261,6 @@ static int imx8ulp_dsp_reset(struct imx_dsp_rproc *priv)
return 0;
}
/* Specific configuration for i.MX8MP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8mp = {
.att = imx_dsp_rproc_att_imx8mp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8mp),
.method = IMX_RPROC_RESET_CONTROLLER,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8mp = {
.dcfg = &dsp_rproc_cfg_imx8mp,
.reset = imx8mp_dsp_reset,
};
/* Specific configuration for i.MX8ULP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8ulp = {
.src_reg = IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
.src_mask = IMX8ULP_SYSCTRL0_DSP_STALL,
.src_start = 0,
.src_stop = IMX8ULP_SYSCTRL0_DSP_STALL,
.att = imx_dsp_rproc_att_imx8ulp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8ulp),
.method = IMX_RPROC_MMIO,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8ulp = {
.dcfg = &dsp_rproc_cfg_imx8ulp,
.reset = imx8ulp_dsp_reset,
};
/* Specific configuration for i.MX8QXP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qxp = {
.att = imx_dsp_rproc_att_imx8qxp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8qxp),
.method = IMX_RPROC_SCU_API,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qxp = {
.dcfg = &dsp_rproc_cfg_imx8qxp,
};
/* Specific configuration for i.MX8QM */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qm = {
.att = imx_dsp_rproc_att_imx8qm,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8qm),
.method = IMX_RPROC_SCU_API,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qm = {
.dcfg = &dsp_rproc_cfg_imx8qm,
};
static int imx_dsp_rproc_ready(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
@@ -388,6 +338,28 @@ static int imx_dsp_rproc_handle_rsc(struct rproc *rproc, u32 rsc_type,
return RSC_HANDLED;
}
static int imx_dsp_rproc_mmio_start(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dsp_dcfg->dcfg;
return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_start);
}
static int imx_dsp_rproc_reset_ctrl_start(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
return reset_control_deassert(priv->run_stall);
}
static int imx_dsp_rproc_scu_api_start(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
return imx_sc_pm_cpu_start(priv->ipc_handle, IMX_SC_R_DSP, true, rproc->bootaddr);
}
/*
* Start function for rproc_ops
*
@@ -404,32 +376,41 @@ static int imx_dsp_rproc_start(struct rproc *rproc)
struct device *dev = rproc->dev.parent;
int ret;
switch (dcfg->method) {
case IMX_RPROC_MMIO:
ret = regmap_update_bits(priv->regmap,
dcfg->src_reg,
dcfg->src_mask,
dcfg->src_start);
break;
case IMX_RPROC_SCU_API:
ret = imx_sc_pm_cpu_start(priv->ipc_handle,
IMX_SC_R_DSP,
true,
rproc->bootaddr);
break;
case IMX_RPROC_RESET_CONTROLLER:
ret = reset_control_deassert(priv->run_stall);
break;
default:
if (!dcfg->ops || !dcfg->ops->start)
return -EOPNOTSUPP;
ret = dcfg->ops->start(rproc);
if (ret) {
dev_err(dev, "Failed to enable remote core!\n");
return ret;
}
if (ret)
dev_err(dev, "Failed to enable remote core!\n");
else if (priv->flags & WAIT_FW_READY)
if (priv->flags & WAIT_FW_READY)
return imx_dsp_rproc_ready(rproc);
return ret;
return 0;
}
static int imx_dsp_rproc_mmio_stop(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dsp_dcfg->dcfg;
return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_stop);
}
static int imx_dsp_rproc_reset_ctrl_stop(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
return reset_control_assert(priv->run_stall);
}
static int imx_dsp_rproc_scu_api_stop(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
return imx_sc_pm_cpu_start(priv->ipc_handle, IMX_SC_R_DSP, false, rproc->bootaddr);
}
/*
@@ -449,30 +430,18 @@ static int imx_dsp_rproc_stop(struct rproc *rproc)
return 0;
}
switch (dcfg->method) {
case IMX_RPROC_MMIO:
ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask,
dcfg->src_stop);
break;
case IMX_RPROC_SCU_API:
ret = imx_sc_pm_cpu_start(priv->ipc_handle,
IMX_SC_R_DSP,
false,
rproc->bootaddr);
break;
case IMX_RPROC_RESET_CONTROLLER:
ret = reset_control_assert(priv->run_stall);
break;
default:
if (!dcfg->ops || !dcfg->ops->stop)
return -EOPNOTSUPP;
ret = dcfg->ops->stop(rproc);
if (ret) {
dev_err(dev, "Failed to stop remote core\n");
return ret;
}
if (ret)
dev_err(dev, "Failed to stop remote core\n");
else
priv->flags &= ~REMOTE_IS_READY;
priv->flags &= ~REMOTE_IS_READY;
return ret;
return 0;
}
/**
@@ -689,11 +658,9 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
struct rproc *rproc = priv->rproc;
struct device *dev = rproc->dev.parent;
struct device_node *np = dev->of_node;
struct of_phandle_iterator it;
struct rproc_mem_entry *mem;
struct reserved_mem *rmem;
void __iomem *cpu_addr;
int a;
int a, i = 0;
u64 da;
/* Remap required addresses */
@@ -724,49 +691,40 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
rproc_add_carveout(rproc, mem);
}
of_phandle_iterator_init(&it, np, "memory-region", NULL, 0);
while (of_phandle_iterator_next(&it) == 0) {
while (1) {
int err;
struct resource res;
err = of_reserved_mem_region_to_resource(np, i++, &res);
if (err)
return 0;
/*
* Ignore the first memory region which will be used vdev buffer.
* No need to do extra handlings, rproc_add_virtio_dev will handle it.
*/
if (!strcmp(it.node->name, "vdev0buffer"))
if (strstarts(res.name, "vdev0buffer"))
continue;
rmem = of_reserved_mem_lookup(it.node);
if (!rmem) {
of_node_put(it.node);
dev_err(dev, "unable to acquire memory-region\n");
if (imx_dsp_rproc_sys_to_da(priv, res.start, resource_size(&res), &da))
return -EINVAL;
}
if (imx_dsp_rproc_sys_to_da(priv, rmem->base, rmem->size, &da)) {
of_node_put(it.node);
return -EINVAL;
}
cpu_addr = devm_ioremap_wc(dev, rmem->base, rmem->size);
if (!cpu_addr) {
of_node_put(it.node);
dev_err(dev, "failed to map memory %p\n", &rmem->base);
return -ENOMEM;
cpu_addr = devm_ioremap_resource_wc(dev, &res);
if (IS_ERR(cpu_addr)) {
dev_err(dev, "failed to map memory %pR\n", &res);
return PTR_ERR(cpu_addr);
}
/* Register memory region */
mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)rmem->base,
rmem->size, da, NULL, NULL, it.node->name);
if (mem) {
rproc_coredump_add_segment(rproc, da, rmem->size);
} else {
of_node_put(it.node);
mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)res.start,
resource_size(&res), da, NULL, NULL,
"%.*s", strchrnul(res.name, '@') - res.name, res.name);
if (!mem)
return -ENOMEM;
}
rproc_coredump_add_segment(rproc, da, resource_size(&res));
rproc_add_carveout(rproc, mem);
}
return 0;
}
/* Prepare function for rproc_ops */
@@ -784,7 +742,7 @@ static int imx_dsp_rproc_prepare(struct rproc *rproc)
pm_runtime_get_sync(dev);
return 0;
return 0;
}
/* Unprepare function for rproc_ops */
@@ -792,7 +750,7 @@ static int imx_dsp_rproc_unprepare(struct rproc *rproc)
{
pm_runtime_put_sync(rproc->dev.parent);
return 0;
return 0;
}
/* Kick function for rproc_ops */
@@ -1062,14 +1020,50 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
static int imx_dsp_attach_pm_domains(struct imx_dsp_rproc *priv)
{
struct device *dev = priv->rproc->dev.parent;
int ret;
/* A single PM domain is already attached. */
if (dev->pm_domain)
return 0;
ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
return ret < 0 ? ret : 0;
return devm_pm_domain_attach_list(dev, NULL, &priv->pd_list);
}
static int imx_dsp_rproc_mmio_detect_mode(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
struct device *dev = rproc->dev.parent;
struct regmap *regmap;
regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,dsp-ctrl");
if (IS_ERR(regmap)) {
dev_err(dev, "failed to find syscon\n");
return PTR_ERR(regmap);
}
priv->regmap = regmap;
return 0;
}
static int imx_dsp_rproc_reset_ctrl_detect_mode(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
struct device *dev = rproc->dev.parent;
priv->run_stall = devm_reset_control_get_exclusive(dev, "runstall");
if (IS_ERR(priv->run_stall)) {
dev_err(dev, "Failed to get DSP runstall reset control\n");
return PTR_ERR(priv->run_stall);
}
return 0;
}
static int imx_dsp_rproc_scu_api_detect_mode(struct rproc *rproc)
{
struct imx_dsp_rproc *priv = rproc->priv;
return imx_scu_get_handle(&priv->ipc_handle);
}
/**
@@ -1087,38 +1081,12 @@ static int imx_dsp_attach_pm_domains(struct imx_dsp_rproc *priv)
static int imx_dsp_rproc_detect_mode(struct imx_dsp_rproc *priv)
{
const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
struct device *dev = priv->rproc->dev.parent;
struct regmap *regmap;
int ret = 0;
const struct imx_rproc_dcfg *dcfg = dsp_dcfg->dcfg;
switch (dsp_dcfg->dcfg->method) {
case IMX_RPROC_SCU_API:
ret = imx_scu_get_handle(&priv->ipc_handle);
if (ret)
return ret;
break;
case IMX_RPROC_MMIO:
regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,dsp-ctrl");
if (IS_ERR(regmap)) {
dev_err(dev, "failed to find syscon\n");
return PTR_ERR(regmap);
}
if (dcfg->ops && dcfg->ops->detect_mode)
return dcfg->ops->detect_mode(priv->rproc);
priv->regmap = regmap;
break;
case IMX_RPROC_RESET_CONTROLLER:
priv->run_stall = devm_reset_control_get_exclusive(dev, "runstall");
if (IS_ERR(priv->run_stall)) {
dev_err(dev, "Failed to get DSP runstall reset control\n");
return PTR_ERR(priv->run_stall);
}
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
return -EOPNOTSUPP;
}
static const char *imx_dsp_clks_names[DSP_RPROC_CLK_MAX] = {
@@ -1152,11 +1120,8 @@ static int imx_dsp_rproc_probe(struct platform_device *pdev)
return -ENODEV;
ret = rproc_of_parse_firmware(dev, 0, &fw_name);
if (ret) {
dev_err(dev, "failed to parse firmware-name property, ret = %d\n",
ret);
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed to parse firmware-name property\n");
rproc = devm_rproc_alloc(dev, "imx-dsp-rproc", &imx_dsp_rproc_ops,
fw_name, sizeof(*priv));
@@ -1179,52 +1144,28 @@ static int imx_dsp_rproc_probe(struct platform_device *pdev)
INIT_WORK(&priv->rproc_work, imx_dsp_rproc_vq_work);
ret = imx_dsp_rproc_detect_mode(priv);
if (ret) {
dev_err(dev, "failed on imx_dsp_rproc_detect_mode\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed on imx_dsp_rproc_detect_mode\n");
/* There are multiple power domains required by DSP on some platform */
ret = imx_dsp_attach_pm_domains(priv);
if (ret) {
dev_err(dev, "failed on imx_dsp_attach_pm_domains\n");
return ret;
}
if (ret < 0)
return dev_err_probe(dev, ret, "failed on imx_dsp_attach_pm_domains\n");
/* Get clocks */
ret = imx_dsp_rproc_clk_get(priv);
if (ret) {
dev_err(dev, "failed on imx_dsp_rproc_clk_get\n");
goto err_detach_domains;
}
if (ret)
return dev_err_probe(dev, ret, "failed on imx_dsp_rproc_clk_get\n");
init_completion(&priv->pm_comp);
rproc->auto_boot = false;
ret = rproc_add(rproc);
if (ret) {
dev_err(dev, "rproc_add failed\n");
goto err_detach_domains;
}
ret = devm_rproc_add(dev, rproc);
if (ret)
return dev_err_probe(dev, ret, "rproc_add failed\n");
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_XTENSA);
pm_runtime_enable(dev);
return 0;
err_detach_domains:
dev_pm_domain_detach_list(priv->pd_list);
return ret;
}
static void imx_dsp_rproc_remove(struct platform_device *pdev)
{
struct rproc *rproc = platform_get_drvdata(pdev);
struct imx_dsp_rproc *priv = rproc->priv;
pm_runtime_disable(&pdev->dev);
rproc_del(rproc);
dev_pm_domain_detach_list(priv->pd_list);
return devm_pm_runtime_enable(dev);
}
/* pm runtime functions */
@@ -1364,6 +1305,74 @@ static const struct dev_pm_ops imx_dsp_rproc_pm_ops = {
RUNTIME_PM_OPS(imx_dsp_runtime_suspend, imx_dsp_runtime_resume, NULL)
};
static const struct imx_rproc_plat_ops imx_dsp_rproc_ops_mmio = {
.start = imx_dsp_rproc_mmio_start,
.stop = imx_dsp_rproc_mmio_stop,
.detect_mode = imx_dsp_rproc_mmio_detect_mode,
};
static const struct imx_rproc_plat_ops imx_dsp_rproc_ops_reset_ctrl = {
.start = imx_dsp_rproc_reset_ctrl_start,
.stop = imx_dsp_rproc_reset_ctrl_stop,
.detect_mode = imx_dsp_rproc_reset_ctrl_detect_mode,
};
static const struct imx_rproc_plat_ops imx_dsp_rproc_ops_scu_api = {
.start = imx_dsp_rproc_scu_api_start,
.stop = imx_dsp_rproc_scu_api_stop,
.detect_mode = imx_dsp_rproc_scu_api_detect_mode,
};
/* Specific configuration for i.MX8MP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8mp = {
.att = imx_dsp_rproc_att_imx8mp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8mp),
.ops = &imx_dsp_rproc_ops_reset_ctrl,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8mp = {
.dcfg = &dsp_rproc_cfg_imx8mp,
.reset = imx8mp_dsp_reset,
};
/* Specific configuration for i.MX8ULP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8ulp = {
.src_reg = IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
.src_mask = IMX8ULP_SYSCTRL0_DSP_STALL,
.src_start = 0,
.src_stop = IMX8ULP_SYSCTRL0_DSP_STALL,
.att = imx_dsp_rproc_att_imx8ulp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8ulp),
.ops = &imx_dsp_rproc_ops_mmio,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8ulp = {
.dcfg = &dsp_rproc_cfg_imx8ulp,
.reset = imx8ulp_dsp_reset,
};
/* Specific configuration for i.MX8QXP */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qxp = {
.att = imx_dsp_rproc_att_imx8qxp,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8qxp),
.ops = &imx_dsp_rproc_ops_scu_api,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qxp = {
.dcfg = &dsp_rproc_cfg_imx8qxp,
};
/* Specific configuration for i.MX8QM */
static const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qm = {
.att = imx_dsp_rproc_att_imx8qm,
.att_size = ARRAY_SIZE(imx_dsp_rproc_att_imx8qm),
.ops = &imx_dsp_rproc_ops_scu_api,
};
static const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qm = {
.dcfg = &dsp_rproc_cfg_imx8qm,
};
static const struct of_device_id imx_dsp_rproc_of_match[] = {
{ .compatible = "fsl,imx8qxp-hifi4", .data = &imx_dsp_rproc_cfg_imx8qxp },
{ .compatible = "fsl,imx8qm-hifi4", .data = &imx_dsp_rproc_cfg_imx8qm },
@@ -1375,7 +1384,6 @@ MODULE_DEVICE_TABLE(of, imx_dsp_rproc_of_match);
static struct platform_driver imx_dsp_rproc_driver = {
.probe = imx_dsp_rproc_probe,
.remove = imx_dsp_rproc_remove,
.driver = {
.name = "imx-dsp-rproc",
.of_match_table = imx_dsp_rproc_of_match,

View File

@@ -93,7 +93,7 @@ struct imx_rproc_mem {
#define ATT_CORE(I) BIT((I))
static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block);
static void imx_rproc_free_mbox(struct rproc *rproc);
static void imx_rproc_free_mbox(void *data);
struct imx_rproc {
struct device *dev;
@@ -490,50 +490,44 @@ static int imx_rproc_prepare(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
struct device_node *np = priv->dev->of_node;
struct of_phandle_iterator it;
struct rproc_mem_entry *mem;
struct reserved_mem *rmem;
int i = 0;
u32 da;
/* Register associated reserved memory regions */
of_phandle_iterator_init(&it, np, "memory-region", NULL, 0);
while (of_phandle_iterator_next(&it) == 0) {
while (1) {
int err;
struct resource res;
err = of_reserved_mem_region_to_resource(np, i++, &res);
if (err)
return 0;
/*
* Ignore the first memory region which will be used vdev buffer.
* No need to do extra handlings, rproc_add_virtio_dev will handle it.
*/
if (!strcmp(it.node->name, "vdev0buffer"))
if (strstarts(res.name, "vdev0buffer"))
continue;
if (!strcmp(it.node->name, "rsc-table"))
if (strstarts(res.name, "rsc-table"))
continue;
rmem = of_reserved_mem_lookup(it.node);
if (!rmem) {
of_node_put(it.node);
dev_err(priv->dev, "unable to acquire memory-region\n");
return -EINVAL;
}
/* No need to translate pa to da, i.MX use same map */
da = rmem->base;
da = res.start;
/* Register memory region */
mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da,
mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start,
resource_size(&res), da,
imx_rproc_mem_alloc, imx_rproc_mem_release,
it.node->name);
if (mem) {
rproc_coredump_add_segment(rproc, da, rmem->size);
} else {
of_node_put(it.node);
"%.*s", strchrnul(res.name, '@') - res.name,
res.name);
if (!mem)
return -ENOMEM;
}
rproc_coredump_add_segment(rproc, da, resource_size(&res));
rproc_add_carveout(rproc, mem);
}
return 0;
}
static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
@@ -575,13 +569,9 @@ static int imx_rproc_attach(struct rproc *rproc)
return imx_rproc_xtr_mbox_init(rproc, true);
}
static int imx_rproc_detach(struct rproc *rproc)
static int imx_rproc_scu_api_detach(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
if (dcfg->method != IMX_RPROC_SCU_API)
return -EOPNOTSUPP;
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id))
return -EOPNOTSUPP;
@@ -591,6 +581,17 @@ static int imx_rproc_detach(struct rproc *rproc)
return 0;
}
static int imx_rproc_detach(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
if (!dcfg->ops || !dcfg->ops->detach)
return -EOPNOTSUPP;
return dcfg->ops->detach(rproc);
}
static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz)
{
struct imx_rproc *priv = rproc->priv;
@@ -664,47 +665,37 @@ static int imx_rproc_addr_init(struct imx_rproc *priv,
}
/* memory-region is optional property */
nph = of_count_phandle_with_args(np, "memory-region", NULL);
nph = of_reserved_mem_region_count(np);
if (nph <= 0)
return 0;
/* remap optional addresses */
for (a = 0; a < nph; a++) {
struct device_node *node;
struct resource res;
node = of_parse_phandle(np, "memory-region", a);
if (!node)
continue;
/* Not map vdevbuffer, vdevring region */
if (!strncmp(node->name, "vdev", strlen("vdev"))) {
of_node_put(node);
continue;
}
err = of_address_to_resource(node, 0, &res);
err = of_reserved_mem_region_to_resource(np, a, &res);
if (err) {
dev_err(dev, "unable to resolve memory region\n");
of_node_put(node);
return err;
}
if (b >= IMX_RPROC_MEM_MAX) {
of_node_put(node);
/* Not map vdevbuffer, vdevring region */
if (strstarts(res.name, "vdev"))
continue;
if (b >= IMX_RPROC_MEM_MAX)
break;
}
/* Not use resource version, because we might share region */
priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, res.start, resource_size(&res));
priv->mem[b].cpu_addr = devm_ioremap_resource_wc(&pdev->dev, &res);
if (!priv->mem[b].cpu_addr) {
dev_err(dev, "failed to remap %pr\n", &res);
of_node_put(node);
return -ENOMEM;
}
priv->mem[b].sys_addr = res.start;
priv->mem[b].size = resource_size(&res);
if (!strcmp(node->name, "rsc-table"))
if (!strcmp(res.name, "rsc-table"))
priv->rsc_table = priv->mem[b].cpu_addr;
of_node_put(node);
b++;
}
@@ -780,8 +771,9 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block)
return 0;
}
static void imx_rproc_free_mbox(struct rproc *rproc)
static void imx_rproc_free_mbox(void *data)
{
struct rproc *rproc = data;
struct imx_rproc *priv = rproc->priv;
if (priv->tx_ch) {
@@ -795,13 +787,9 @@ static void imx_rproc_free_mbox(struct rproc *rproc)
}
}
static void imx_rproc_put_scu(struct rproc *rproc)
static void imx_rproc_put_scu(void *data)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
if (dcfg->method != IMX_RPROC_SCU_API)
return;
struct imx_rproc *priv = data;
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
dev_pm_domain_detach_list(priv->pd_list);
@@ -943,6 +931,10 @@ static int imx_rproc_scu_api_detect_mode(struct rproc *rproc)
else
priv->core_index = 0;
ret = devm_add_action_or_reset(dev, imx_rproc_put_scu, priv);
if (ret)
return dev_err_probe(dev, ret, "Failed to add action for put scu\n");
/*
* If Mcore resource is not owned by Acore partition, It is kicked by ROM,
* and Linux could only do IPC with Mcore and nothing else.
@@ -1001,35 +993,6 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv)
return dcfg->ops->detect_mode(priv->rproc);
}
static int imx_rproc_clk_enable(struct imx_rproc *priv)
{
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
struct device *dev = priv->dev;
int ret;
/* Remote core is not under control of Linux or it is managed by SCU API */
if (dcfg->method == IMX_RPROC_NONE || dcfg->method == IMX_RPROC_SCU_API)
return 0;
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "Failed to get clock\n");
return PTR_ERR(priv->clk);
}
/*
* clk for M4 block including memory. Should be
* enabled before .start for FW transfer.
*/
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "Failed to enable clock\n");
return ret;
}
return 0;
}
static int imx_rproc_sys_off_handler(struct sys_off_data *data)
{
struct rproc *rproc = data->cb_data;
@@ -1046,6 +1009,13 @@ static int imx_rproc_sys_off_handler(struct sys_off_data *data)
return NOTIFY_DONE;
}
static void imx_rproc_destroy_workqueue(void *data)
{
struct workqueue_struct *workqueue = data;
destroy_workqueue(workqueue);
}
static int imx_rproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1077,25 +1047,38 @@ static int imx_rproc_probe(struct platform_device *pdev)
return -ENOMEM;
}
ret = devm_add_action_or_reset(dev, imx_rproc_destroy_workqueue, priv->workqueue);
if (ret)
return dev_err_probe(dev, ret, "Failed to add devm destroy workqueue action\n");
INIT_WORK(&priv->rproc_work, imx_rproc_vq_work);
ret = imx_rproc_xtr_mbox_init(rproc, true);
if (ret)
goto err_put_wkq;
return ret;
ret = devm_add_action_or_reset(dev, imx_rproc_free_mbox, rproc);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add devm free mbox action: %d\n", ret);
ret = imx_rproc_addr_init(priv, pdev);
if (ret) {
dev_err(dev, "failed on imx_rproc_addr_init\n");
goto err_put_mbox;
}
if (ret)
return dev_err_probe(dev, ret, "failed on imx_rproc_addr_init\n");
ret = imx_rproc_detect_mode(priv);
if (ret)
goto err_put_mbox;
return dev_err_probe(dev, ret, "failed on detect mode\n");
ret = imx_rproc_clk_enable(priv);
if (ret)
goto err_put_scu;
/*
* Handle clocks when remote core is under control of Linux AND the
* clocks are not managed by system firmware.
*/
if (dcfg->flags & IMX_RPROC_NEED_CLKS) {
priv->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock\n");
}
if (rproc->state != RPROC_DETACHED)
rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot");
@@ -1110,45 +1093,32 @@ static int imx_rproc_probe(struct platform_device *pdev)
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF_PREPARE,
SYS_OFF_PRIO_DEFAULT,
imx_rproc_sys_off_handler, rproc);
if (ret) {
dev_err(dev, "register power off handler failure\n");
goto err_put_clk;
}
if (ret)
return dev_err_probe(dev, ret, "register power off handler failure\n");
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART_PREPARE,
SYS_OFF_PRIO_DEFAULT,
imx_rproc_sys_off_handler, rproc);
if (ret) {
dev_err(dev, "register restart handler failure\n");
goto err_put_clk;
}
if (ret)
return dev_err_probe(dev, ret, "register restart handler failure\n");
}
if (dcfg->method == IMX_RPROC_SCU_API) {
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret) {
dev_err(dev, "pm_runtime get failed: %d\n", ret);
goto err_put_clk;
}
}
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret)
return dev_err_probe(dev, ret, "pm_runtime get failed\n");
ret = rproc_add(rproc);
ret = devm_rproc_add(dev, rproc);
if (ret) {
dev_err(dev, "rproc_add failed\n");
goto err_put_clk;
goto err_put_pm;
}
return 0;
err_put_clk:
clk_disable_unprepare(priv->clk);
err_put_scu:
imx_rproc_put_scu(rproc);
err_put_mbox:
imx_rproc_free_mbox(rproc);
err_put_wkq:
destroy_workqueue(priv->workqueue);
err_put_pm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
return ret;
}
@@ -1158,15 +1128,8 @@ static void imx_rproc_remove(struct platform_device *pdev)
struct rproc *rproc = platform_get_drvdata(pdev);
struct imx_rproc *priv = rproc->priv;
if (priv->dcfg->method == IMX_RPROC_SCU_API) {
pm_runtime_disable(priv->dev);
pm_runtime_put(priv->dev);
}
clk_disable_unprepare(priv->clk);
rproc_del(rproc);
imx_rproc_put_scu(rproc);
imx_rproc_free_mbox(rproc);
destroy_workqueue(priv->workqueue);
pm_runtime_disable(priv->dev);
pm_runtime_put_noidle(priv->dev);
}
static const struct imx_rproc_plat_ops imx_rproc_ops_arm_smc = {
@@ -1184,6 +1147,7 @@ static const struct imx_rproc_plat_ops imx_rproc_ops_mmio = {
static const struct imx_rproc_plat_ops imx_rproc_ops_scu_api = {
.start = imx_rproc_scu_api_start,
.stop = imx_rproc_scu_api_stop,
.detach = imx_rproc_scu_api_detach,
.detect_mode = imx_rproc_scu_api_detect_mode,
};
@@ -1196,15 +1160,15 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = {
.gpr_wait = IMX8M_GPR22_CM7_CPUWAIT,
.att = imx_rproc_att_imx8mn,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mn),
.method = IMX_RPROC_MMIO,
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = {
.att = imx_rproc_att_imx8mn,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mn),
.method = IMX_RPROC_SMC,
.ops = &imx_rproc_ops_arm_smc,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = {
@@ -1214,34 +1178,30 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = {
.src_stop = IMX7D_M4_STOP,
.att = imx_rproc_att_imx8mq,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mq),
.method = IMX_RPROC_MMIO,
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qm = {
.att = imx_rproc_att_imx8qm,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8qm),
.method = IMX_RPROC_SCU_API,
.ops = &imx_rproc_ops_scu_api,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qxp = {
.att = imx_rproc_att_imx8qxp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8qxp),
.method = IMX_RPROC_SCU_API,
.ops = &imx_rproc_ops_scu_api,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = {
.att = imx_rproc_att_imx8ulp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8ulp),
.method = IMX_RPROC_NONE,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = {
.att = imx_rproc_att_imx7ulp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp),
.method = IMX_RPROC_NONE,
.flags = IMX_RPROC_NEED_SYSTEM_OFF,
};
@@ -1252,8 +1212,8 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
.src_stop = IMX7D_M4_STOP,
.att = imx_rproc_att_imx7d,
.att_size = ARRAY_SIZE(imx_rproc_att_imx7d),
.method = IMX_RPROC_MMIO,
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
@@ -1263,15 +1223,15 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
.src_stop = IMX6SX_M4_STOP,
.att = imx_rproc_att_imx6sx,
.att_size = ARRAY_SIZE(imx_rproc_att_imx6sx),
.method = IMX_RPROC_MMIO,
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = {
.att = imx_rproc_att_imx93,
.att_size = ARRAY_SIZE(imx_rproc_att_imx93),
.method = IMX_RPROC_SMC,
.ops = &imx_rproc_ops_arm_smc,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct of_device_id imx_rproc_of_match[] = {

View File

@@ -15,25 +15,14 @@ struct imx_rproc_att {
int flags;
};
/* Remote core start/stop method */
enum imx_rproc_method {
IMX_RPROC_NONE,
/* Through syscon regmap */
IMX_RPROC_MMIO,
/* Through ARM SMCCC */
IMX_RPROC_SMC,
/* Through System Control Unit API */
IMX_RPROC_SCU_API,
/* Through Reset Controller API */
IMX_RPROC_RESET_CONTROLLER,
};
/* dcfg flags */
#define IMX_RPROC_NEED_SYSTEM_OFF BIT(0)
#define IMX_RPROC_NEED_CLKS BIT(1)
struct imx_rproc_plat_ops {
int (*start)(struct rproc *rproc);
int (*stop)(struct rproc *rproc);
int (*detach)(struct rproc *rproc);
int (*detect_mode)(struct rproc *rproc);
};
@@ -46,7 +35,6 @@ struct imx_rproc_dcfg {
u32 gpr_wait;
const struct imx_rproc_att *att;
size_t att_size;
enum imx_rproc_method method;
u32 flags;
const struct imx_rproc_plat_ops *ops;
};

View File

@@ -16,6 +16,7 @@
#include <linux/remoteproc.h>
#include <linux/remoteproc/mtk_scp.h>
#include <linux/rpmsg/mtk_rpmsg.h>
#include <linux/string.h>
#include "mtk_common.h"
#include "remoteproc_internal.h"
@@ -1093,22 +1094,74 @@ static void scp_remove_rpmsg_subdev(struct mtk_scp *scp)
}
}
/**
* scp_get_default_fw_path() - Get default SCP firmware path
* @dev: SCP Device
* @core_id: SCP Core number
*
* This function generates a path based on the following format:
* mediatek/(soc_model)/scp(_cX).img; for multi-core or
* mediatek/(soc_model)/scp.img for single core SCP HW
*
* Return: A devm allocated string containing the full path to
* a SCP firmware or an error pointer
*/
static const char *scp_get_default_fw_path(struct device *dev, int core_id)
{
struct device_node *np = core_id < 0 ? dev->of_node : dev->parent->of_node;
const char *compatible, *soc;
char scp_fw_file[7];
int ret;
/* Use only the first compatible string */
ret = of_property_read_string_index(np, "compatible", 0, &compatible);
if (ret)
return ERR_PTR(ret);
/* If the compatible string's length is implausible bail out early */
if (strlen(compatible) < strlen("mediatek,mtXXXX-scp"))
return ERR_PTR(-EINVAL);
/* If the compatible string starts with "mediatek,mt" assume that it's ok */
if (!str_has_prefix(compatible, "mediatek,mt"))
return ERR_PTR(-EINVAL);
if (core_id >= 0)
ret = snprintf(scp_fw_file, sizeof(scp_fw_file), "scp_c%d", core_id);
else
ret = snprintf(scp_fw_file, sizeof(scp_fw_file), "scp");
if (ret >= sizeof(scp_fw_file))
return ERR_PTR(-ENAMETOOLONG);
/* Not using strchr here, as strlen of a const gets optimized by compiler */
soc = &compatible[strlen("mediatek,")];
return devm_kasprintf(dev, GFP_KERNEL, "mediatek/%.*s/%s.img",
(int)strlen("mtXXXX"), soc, scp_fw_file);
}
static struct mtk_scp *scp_rproc_init(struct platform_device *pdev,
struct mtk_scp_of_cluster *scp_cluster,
const struct mtk_scp_of_data *of_data)
const struct mtk_scp_of_data *of_data,
int core_id)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct mtk_scp *scp;
struct rproc *rproc;
struct resource *res;
const char *fw_name = "scp.img";
const char *fw_name;
int ret, i;
const struct mtk_scp_sizes_data *scp_sizes;
ret = rproc_of_parse_firmware(dev, 0, &fw_name);
if (ret < 0 && ret != -EINVAL)
return ERR_PTR(ret);
if (ret) {
fw_name = scp_get_default_fw_path(dev, core_id);
if (IS_ERR(fw_name)) {
dev_err(dev, "Cannot get firmware path: %ld\n", PTR_ERR(fw_name));
return ERR_CAST(fw_name);
}
}
rproc = devm_rproc_alloc(dev, np->name, &scp_ops, fw_name, sizeof(*scp));
if (!rproc) {
@@ -1212,7 +1265,7 @@ static int scp_add_single_core(struct platform_device *pdev,
struct mtk_scp *scp;
int ret;
scp = scp_rproc_init(pdev, scp_cluster, of_device_get_match_data(dev));
scp = scp_rproc_init(pdev, scp_cluster, of_device_get_match_data(dev), -1);
if (IS_ERR(scp))
return PTR_ERR(scp);
@@ -1259,7 +1312,7 @@ static int scp_add_multi_core(struct platform_device *pdev,
goto init_fail;
}
scp = scp_rproc_init(cpdev, scp_cluster, cluster_of_data[core_id]);
scp = scp_rproc_init(cpdev, scp_cluster, cluster_of_data[core_id], core_id);
put_device(&cpdev->dev);
if (IS_ERR(scp)) {
ret = PTR_ERR(scp);

View File

@@ -555,7 +555,6 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid)
dev_err(dev, "failed to send mailbox message, status = %d\n",
ret);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
@@ -656,7 +655,6 @@ static int omap_rproc_start(struct rproc *rproc)
pm_runtime_use_autosuspend(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -714,7 +712,6 @@ enable_device:
reset_control_deassert(oproc->reset);
out:
/* schedule the next auto-suspend */
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}

View File

@@ -625,27 +625,22 @@ static int adsp_init_mmio(struct qcom_adsp *adsp,
static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
{
struct reserved_mem *rmem = NULL;
struct device_node *node;
int ret;
struct resource res;
node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
if (node)
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
ret = of_reserved_mem_region_to_resource(adsp->dev->of_node, 0, &res);
if (ret) {
dev_err(adsp->dev, "unable to resolve memory-region\n");
return -EINVAL;
return ret;
}
adsp->mem_phys = adsp->mem_reloc = rmem->base;
adsp->mem_size = rmem->size;
adsp->mem_region = devm_ioremap_wc(adsp->dev,
adsp->mem_phys, adsp->mem_size);
if (!adsp->mem_region) {
dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
&rmem->base, adsp->mem_size);
return -EBUSY;
adsp->mem_phys = adsp->mem_reloc = res.start;
adsp->mem_size = resource_size(&res);
adsp->mem_region = devm_ioremap_resource_wc(adsp->dev, &res);
if (IS_ERR(adsp->mem_region)) {
dev_err(adsp->dev, "unable to map memory region: %pR\n", &res);
return PTR_ERR(adsp->mem_region);
}
return 0;

View File

@@ -1970,8 +1970,8 @@ static int q6v5_init_reset(struct q6v5 *qproc)
static int q6v5_alloc_memory_region(struct q6v5 *qproc)
{
struct device_node *child;
struct reserved_mem *rmem;
struct device_node *node;
struct resource res;
int ret;
/*
* In the absence of mba/mpss sub-child, extract the mba and mpss
@@ -1979,71 +1979,49 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
*/
child = of_get_child_by_name(qproc->dev->of_node, "mba");
if (!child) {
node = of_parse_phandle(qproc->dev->of_node,
"memory-region", 0);
ret = of_reserved_mem_region_to_resource(qproc->dev->of_node, 0, &res);
} else {
node = of_parse_phandle(child, "memory-region", 0);
ret = of_reserved_mem_region_to_resource(child, 0, &res);
of_node_put(child);
}
if (!node) {
dev_err(qproc->dev, "no mba memory-region specified\n");
return -EINVAL;
}
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
if (ret) {
dev_err(qproc->dev, "unable to resolve mba region\n");
return -EINVAL;
return ret;
}
qproc->mba_phys = rmem->base;
qproc->mba_size = rmem->size;
qproc->mba_phys = res.start;
qproc->mba_size = resource_size(&res);
if (!child) {
node = of_parse_phandle(qproc->dev->of_node,
"memory-region", 1);
ret = of_reserved_mem_region_to_resource(qproc->dev->of_node, 1, &res);
} else {
child = of_get_child_by_name(qproc->dev->of_node, "mpss");
node = of_parse_phandle(child, "memory-region", 0);
ret = of_reserved_mem_region_to_resource(child, 0, &res);
of_node_put(child);
}
if (!node) {
dev_err(qproc->dev, "no mpss memory-region specified\n");
return -EINVAL;
}
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
if (ret) {
dev_err(qproc->dev, "unable to resolve mpss region\n");
return -EINVAL;
return ret;
}
qproc->mpss_phys = qproc->mpss_reloc = rmem->base;
qproc->mpss_size = rmem->size;
qproc->mpss_phys = qproc->mpss_reloc = res.start;
qproc->mpss_size = resource_size(&res);
if (!child) {
node = of_parse_phandle(qproc->dev->of_node, "memory-region", 2);
ret = of_reserved_mem_region_to_resource(qproc->dev->of_node, 2, &res);
} else {
child = of_get_child_by_name(qproc->dev->of_node, "metadata");
node = of_parse_phandle(child, "memory-region", 0);
ret = of_reserved_mem_region_to_resource(child, 0, &res);
of_node_put(child);
}
if (!node)
if (ret)
return 0;
rmem = of_reserved_mem_lookup(node);
if (!rmem) {
dev_err(qproc->dev, "unable to resolve metadata region\n");
return -EINVAL;
}
qproc->mdata_phys = rmem->base;
qproc->mdata_size = rmem->size;
qproc->mdata_phys = res.start;
qproc->mdata_size = resource_size(&res);
return 0;
}

View File

@@ -547,54 +547,38 @@ static void qcom_pas_pds_detach(struct qcom_pas *pas, struct device **pds, size_
static int qcom_pas_alloc_memory_region(struct qcom_pas *pas)
{
struct reserved_mem *rmem;
struct device_node *node;
struct resource res;
int ret;
node = of_parse_phandle(pas->dev->of_node, "memory-region", 0);
if (!node) {
dev_err(pas->dev, "no memory-region specified\n");
return -EINVAL;
}
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
ret = of_reserved_mem_region_to_resource(pas->dev->of_node, 0, &res);
if (ret) {
dev_err(pas->dev, "unable to resolve memory-region\n");
return -EINVAL;
return ret;
}
pas->mem_phys = pas->mem_reloc = rmem->base;
pas->mem_size = rmem->size;
pas->mem_region = devm_ioremap_wc(pas->dev, pas->mem_phys, pas->mem_size);
if (!pas->mem_region) {
dev_err(pas->dev, "unable to map memory region: %pa+%zx\n",
&rmem->base, pas->mem_size);
return -EBUSY;
pas->mem_phys = pas->mem_reloc = res.start;
pas->mem_size = resource_size(&res);
pas->mem_region = devm_ioremap_resource_wc(pas->dev, &res);
if (IS_ERR(pas->mem_region)) {
dev_err(pas->dev, "unable to map memory region: %pR\n", &res);
return PTR_ERR(pas->mem_region);
}
if (!pas->dtb_pas_id)
return 0;
node = of_parse_phandle(pas->dev->of_node, "memory-region", 1);
if (!node) {
dev_err(pas->dev, "no dtb memory-region specified\n");
return -EINVAL;
}
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
ret = of_reserved_mem_region_to_resource(pas->dev->of_node, 1, &res);
if (ret) {
dev_err(pas->dev, "unable to resolve dtb memory-region\n");
return -EINVAL;
return ret;
}
pas->dtb_mem_phys = pas->dtb_mem_reloc = rmem->base;
pas->dtb_mem_size = rmem->size;
pas->dtb_mem_region = devm_ioremap_wc(pas->dev, pas->dtb_mem_phys, pas->dtb_mem_size);
if (!pas->dtb_mem_region) {
dev_err(pas->dev, "unable to map dtb memory region: %pa+%zx\n",
&rmem->base, pas->dtb_mem_size);
return -EBUSY;
pas->dtb_mem_phys = pas->dtb_mem_reloc = res.start;
pas->dtb_mem_size = resource_size(&res);
pas->dtb_mem_region = devm_ioremap_resource_wc(pas->dev, &res);
if (IS_ERR(pas->dtb_mem_region)) {
dev_err(pas->dev, "unable to map dtb memory region: %pR\n", &res);
return PTR_ERR(pas->dtb_mem_region);
}
return 0;
@@ -603,7 +587,6 @@ static int qcom_pas_alloc_memory_region(struct qcom_pas *pas)
static int qcom_pas_assign_memory_region(struct qcom_pas *pas)
{
struct qcom_scm_vmperm perm[MAX_ASSIGN_COUNT];
struct device_node *node;
unsigned int perm_size;
int offset;
int ret;
@@ -612,17 +595,15 @@ static int qcom_pas_assign_memory_region(struct qcom_pas *pas)
return 0;
for (offset = 0; offset < pas->region_assign_count; ++offset) {
struct reserved_mem *rmem = NULL;
struct resource res;
node = of_parse_phandle(pas->dev->of_node, "memory-region",
pas->region_assign_idx + offset);
if (node)
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem) {
ret = of_reserved_mem_region_to_resource(pas->dev->of_node,
pas->region_assign_idx + offset,
&res);
if (ret) {
dev_err(pas->dev, "unable to resolve shareable memory-region index %d\n",
offset);
return -EINVAL;
return ret;
}
if (pas->region_assign_shared) {
@@ -637,8 +618,8 @@ static int qcom_pas_assign_memory_region(struct qcom_pas *pas)
perm_size = 1;
}
pas->region_assign_phys[offset] = rmem->base;
pas->region_assign_size[offset] = rmem->size;
pas->region_assign_phys[offset] = res.start;
pas->region_assign_size[offset] = resource_size(&res);
pas->region_assign_owners[offset] = BIT(QCOM_SCM_VMID_HLOS);
ret = qcom_scm_assign_mem(pas->region_assign_phys[offset],
@@ -1461,7 +1442,7 @@ static const struct of_device_id qcom_pas_of_match[] = {
{ .compatible = "qcom,milos-wpss-pas", .data = &sc7280_wpss_resource},
{ .compatible = "qcom,msm8226-adsp-pil", .data = &msm8996_adsp_resource},
{ .compatible = "qcom,msm8953-adsp-pil", .data = &msm8996_adsp_resource},
{ .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
{ .compatible = "qcom,msm8974-adsp-pil", .data = &msm8996_adsp_resource},
{ .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource},
{ .compatible = "qcom,msm8996-slpi-pil", .data = &msm8996_slpi_resource_init},
{ .compatible = "qcom,msm8998-adsp-pas", .data = &msm8996_adsp_resource},
@@ -1488,6 +1469,7 @@ static const struct of_device_id qcom_pas_of_match[] = {
{ .compatible = "qcom,sc8280xp-nsp0-pas", .data = &sc8280xp_nsp0_resource},
{ .compatible = "qcom,sc8280xp-nsp1-pas", .data = &sc8280xp_nsp1_resource},
{ .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init},
{ .compatible = "qcom,sdm660-cdsp-pas", .data = &cdsp_resource_init},
{ .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init},
{ .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init},
{ .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init},

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