4ec3c2eea5
Changes in 5.10.94
KVM: VMX: switch blocked_vcpu_on_cpu_lock to raw spinlock
HID: uhid: Fix worker destroying device without any protection
HID: wacom: Reset expected and received contact counts at the same time
HID: wacom: Ignore the confidence flag when a touch is removed
HID: wacom: Avoid using stale array indicies to read contact count
f2fs: fix to do sanity check in is_alive()
nfc: llcp: fix NULL error pointer dereference on sendmsg() after failed bind()
mtd: rawnand: gpmi: Add ERR007117 protection for nfc_apply_timings
mtd: rawnand: gpmi: Remove explicit default gpmi clock setting for i.MX6
mtd: Fixed breaking list in __mtd_del_partition.
mtd: rawnand: davinci: Don't calculate ECC when reading page
mtd: rawnand: davinci: Avoid duplicated page read
mtd: rawnand: davinci: Rewrite function description
x86/gpu: Reserve stolen memory for first integrated Intel GPU
tools/nolibc: x86-64: Fix startup code bug
tools/nolibc: i386: fix initial stack alignment
tools/nolibc: fix incorrect truncation of exit code
rtc: cmos: take rtc_lock while reading from CMOS
media: v4l2-ioctl.c: readbuffers depends on V4L2_CAP_READWRITE
media: flexcop-usb: fix control-message timeouts
media: mceusb: fix control-message timeouts
media: em28xx: fix control-message timeouts
media: cpia2: fix control-message timeouts
media: s2255: fix control-message timeouts
media: dib0700: fix undefined behavior in tuner shutdown
media: redrat3: fix control-message timeouts
media: pvrusb2: fix control-message timeouts
media: stk1160: fix control-message timeouts
media: cec-pin: fix interrupt en/disable handling
can: softing_cs: softingcs_probe(): fix memleak on registration failure
iio: adc: ti-adc081c: Partial revert of removal of ACPI IDs
lkdtm: Fix content of section containing lkdtm_rodata_do_nothing()
iommu/io-pgtable-arm-v7s: Add error handle for page table allocation failure
gpu: host1x: Add back arm_iommu_detach_device()
dma_fence_array: Fix PENDING_ERROR leak in dma_fence_array_signaled()
PCI: Add function 1 DMA alias quirk for Marvell 88SE9125 SATA controller
mm_zone: add function to check if managed dma zone exists
dma/pool: create dma atomic pool only if dma zone has managed pages
mm/page_alloc.c: do not warn allocation failure on zone DMA if no managed pages
shmem: fix a race between shmem_unused_huge_shrink and shmem_evict_inode
drm/ttm: Put BO in its memory manager's lru list
Bluetooth: L2CAP: Fix not initializing sk_peer_pid
drm/bridge: display-connector: fix an uninitialized pointer in probe()
drm: fix null-ptr-deref in drm_dev_init_release()
drm/panel: kingdisplay-kd097d04: Delete panel on attach() failure
drm/panel: innolux-p079zca: Delete panel on attach() failure
drm/rockchip: dsi: Fix unbalanced clock on probe error
drm/rockchip: dsi: Hold pm-runtime across bind/unbind
drm/rockchip: dsi: Disable PLL clock on bind error
drm/rockchip: dsi: Reconfigure hardware on resume()
Bluetooth: cmtp: fix possible panic when cmtp_init_sockets() fails
clk: bcm-2835: Pick the closest clock rate
clk: bcm-2835: Remove rounding up the dividers
drm/vc4: hdmi: Set a default HSM rate
wcn36xx: ensure pairing of init_scan/finish_scan and start_scan/end_scan
wcn36xx: Indicate beacon not connection loss on MISSED_BEACON_IND
wcn36xx: Fix DMA channel enable/disable cycle
wcn36xx: Release DMA channel descriptor allocations
wcn36xx: Put DXE block into reset before freeing memory
wcn36xx: populate band before determining rate on RX
wcn36xx: fix RX BD rate mapping for 5GHz legacy rates
ath11k: Send PPDU_STATS_CFG with proper pdev mask to firmware
mtd: hyperbus: rpc-if: Check return value of rpcif_sw_init()
media: videobuf2: Fix the size printk format
media: atomisp: add missing media_device_cleanup() in atomisp_unregister_entities()
media: atomisp: fix punit_ddr_dvfs_enable() argument for mrfld_power up case
media: atomisp: fix inverted logic in buffers_needed()
media: atomisp: do not use err var when checking port validity for ISP2400
media: atomisp: fix inverted error check for ia_css_mipi_is_source_port_valid()
media: atomisp: fix ifdefs in sh_css.c
media: staging: media: atomisp: pci: Balance braces around conditional statements in file atomisp_cmd.c
media: atomisp: add NULL check for asd obtained from atomisp_video_pipe
media: atomisp: fix enum formats logic
media: atomisp: fix uninitialized bug in gmin_get_pmic_id_and_addr()
media: aspeed: fix mode-detect always time out at 2nd run
media: em28xx: fix memory leak in em28xx_init_dev
media: aspeed: Update signal status immediately to ensure sane hw state
arm64: dts: amlogic: meson-g12: Fix GPU operating point table node name
arm64: dts: amlogic: Fix SPI NOR flash node name for ODROID N2/N2+
arm64: dts: meson-gxbb-wetek: fix HDMI in early boot
arm64: dts: meson-gxbb-wetek: fix missing GPIO binding
fs: dlm: use sk->sk_socket instead of con->sock
fs: dlm: don't call kernel_getpeername() in error_report()
memory: renesas-rpc-if: Return error in case devm_ioremap_resource() fails
Bluetooth: stop proccessing malicious adv data
ath11k: Fix ETSI regd with weather radar overlap
ath11k: clear the keys properly via DISABLE_KEY
ath11k: reset RSN/WPA present state for open BSS
tee: fix put order in teedev_close_context()
fs: dlm: fix build with CONFIG_IPV6 disabled
drm/vboxvideo: fix a NULL vs IS_ERR() check
arm64: dts: renesas: cat875: Add rx/tx delays
media: dmxdev: fix UAF when dvb_register_device() fails
crypto: qce - fix uaf on qce_ahash_register_one
crypto: qce - fix uaf on qce_skcipher_register_one
mtd: hyperbus: rpc-if: fix bug in rpcif_hb_remove
ARM: dts: stm32: fix dtbs_check warning on ili9341 dts binding on stm32f429 disco
crypto: qat - fix spelling mistake: "messge" -> "message"
crypto: qat - remove unnecessary collision prevention step in PFVF
crypto: qat - make pfvf send message direction agnostic
crypto: qat - fix undetected PFVF timeout in ACK loop
ath11k: Use host CE parameters for CE interrupts configuration
arm64: dts: ti: k3-j721e: correct cache-sets info
tty: serial: atmel: Check return code of dmaengine_submit()
tty: serial: atmel: Call dma_async_issue_pending()
mfd: atmel-flexcom: Remove #ifdef CONFIG_PM_SLEEP
mfd: atmel-flexcom: Use .resume_noirq
media: rcar-csi2: Correct the selection of hsfreqrange
media: imx-pxp: Initialize the spinlock prior to using it
media: si470x-i2c: fix possible memory leak in si470x_i2c_probe()
media: mtk-vcodec: call v4l2_m2m_ctx_release first when file is released
media: coda: fix CODA960 JPEG encoder buffer overflow
media: venus: pm_helpers: Control core power domain manually
media: venus: core, venc, vdec: Fix probe dependency error
media: venus: core: Fix a potential NULL pointer dereference in an error handling path
media: venus: core: Fix a resource leak in the error handling path of 'venus_probe()'
thermal/drivers/imx: Implement runtime PM support
netfilter: bridge: add support for pppoe filtering
arm64: dts: qcom: msm8916: fix MMC controller aliases
cgroup: Trace event cgroup id fields should be u64
ACPI: EC: Rework flushing of EC work while suspended to idle
thermal/drivers/imx8mm: Enable ADC when enabling monitor
drm/amdgpu: Fix a NULL pointer dereference in amdgpu_connector_lcd_native_mode()
drm/radeon/radeon_kms: Fix a NULL pointer dereference in radeon_driver_open_kms()
arm64: dts: ti: k3-j7200: Fix the L2 cache sets
arm64: dts: ti: k3-j721e: Fix the L2 cache sets
arm64: dts: ti: k3-j7200: Correct the d-cache-sets info
tty: serial: uartlite: allow 64 bit address
serial: amba-pl011: do not request memory region twice
floppy: Fix hang in watchdog when disk is ejected
staging: rtl8192e: return error code from rtllib_softmac_init()
staging: rtl8192e: rtllib_module: fix error handle case in alloc_rtllib()
Bluetooth: btmtksdio: fix resume failure
sched/fair: Fix detection of per-CPU kthreads waking a task
sched/fair: Fix per-CPU kthread and wakee stacking for asym CPU capacity
bpf: Adjust BTF log size limit.
bpf: Disallow BPF_LOG_KERNEL log level for bpf(BPF_BTF_LOAD)
bpf: Remove config check to enable bpf support for branch records
arm64: lib: Annotate {clear, copy}_page() as position-independent
arm64: clear_page() shouldn't use DC ZVA when DCZID_EL0.DZP == 1
media: dib8000: Fix a memleak in dib8000_init()
media: saa7146: mxb: Fix a NULL pointer dereference in mxb_attach()
media: si2157: Fix "warm" tuner state detection
wireless: iwlwifi: Fix a double free in iwl_txq_dyn_alloc_dma
sched/rt: Try to restart rt period timer when rt runtime exceeded
drm/msm/dp: displayPort driver need algorithm rational
rcu/exp: Mark current CPU as exp-QS in IPI loop second pass
mwifiex: Fix possible ABBA deadlock
xfrm: fix a small bug in xfrm_sa_len()
x86/uaccess: Move variable into switch case statement
selftests: clone3: clone3: add case CLONE3_ARGS_NO_TEST
selftests: harness: avoid false negatives if test has no ASSERTs
crypto: stm32 - Fix last sparse warning in stm32_cryp_check_ctr_counter
crypto: stm32/cryp - fix CTR counter carry
crypto: stm32/cryp - fix xts and race condition in crypto_engine requests
crypto: stm32/cryp - check early input data
crypto: stm32/cryp - fix double pm exit
crypto: stm32/cryp - fix lrw chaining mode
crypto: stm32/cryp - fix bugs and crash in tests
crypto: stm32 - Revert broken pm_runtime_resume_and_get changes
ath11k: Fix deleting uninitialized kernel timer during fragment cache flush
ARM: dts: gemini: NAS4220-B: fis-index-block with 128 KiB sectors
media: dw2102: Fix use after free
media: msi001: fix possible null-ptr-deref in msi001_probe()
media: coda/imx-vdoa: Handle dma_set_coherent_mask error codes
ath11k: Fix a NULL pointer dereference in ath11k_mac_op_hw_scan()
arm64: dts: qcom: c630: Fix soundcard setup
arm64: dts: qcom: ipq6018: Fix gpio-ranges property
drm/msm/dpu: fix safe status debugfs file
drm/bridge: ti-sn65dsi86: Set max register for regmap
drm/tegra: vic: Fix DMA API misuse
media: hantro: Fix probe func error path
xfrm: interface with if_id 0 should return error
xfrm: state and policy should fail if XFRMA_IF_ID 0
ARM: 9159/1: decompressor: Avoid UNPREDICTABLE NOP encoding
usb: ftdi-elan: fix memory leak on device disconnect
arm64: dts: marvell: cn9130: add GPIO and SPI aliases
arm64: dts: marvell: cn9130: enable CP0 GPIO controllers
ARM: dts: armada-38x: Add generic compatible to UART nodes
iwlwifi: mvm: fix 32-bit build in FTM
iwlwifi: mvm: test roc running status bits before removing the sta
mmc: meson-mx-sdhc: add IRQ check
mmc: meson-mx-sdio: add IRQ check
selinux: fix potential memleak in selinux_add_opt()
um: fix ndelay/udelay defines
um: virtio_uml: Fix time-travel external time propagation
Bluetooth: L2CAP: Fix using wrong mode
bpftool: Enable line buffering for stdout
backlight: qcom-wled: Validate enabled string indices in DT
backlight: qcom-wled: Pass number of elements to read to read_u32_array
backlight: qcom-wled: Fix off-by-one maximum with default num_strings
backlight: qcom-wled: Override default length with qcom,enabled-strings
backlight: qcom-wled: Use cpu_to_le16 macro to perform conversion
backlight: qcom-wled: Respect enabled-strings in set_brightness
software node: fix wrong node passed to find nargs_prop
Bluetooth: hci_qca: Stop IBS timer during BT OFF
x86/boot/compressed: Move CLANG_FLAGS to beginning of KBUILD_CFLAGS
hwmon: (mr75203) fix wrong power-up delay value
x86/mce/inject: Avoid out-of-bounds write when setting flags
ACPI: scan: Create platform device for BCM4752 and LNV4752 ACPI nodes
pcmcia: rsrc_nonstatic: Fix a NULL pointer dereference in __nonstatic_find_io_region()
pcmcia: rsrc_nonstatic: Fix a NULL pointer dereference in nonstatic_find_mem_region()
power: reset: mt6397: Check for null res pointer
netfilter: ipt_CLUSTERIP: fix refcount leak in clusterip_tg_check()
bpf: Don't promote bogus looking registers after null check.
bpf: Fix SO_RCVBUF/SO_SNDBUF handling in _bpf_setsockopt().
netfilter: nft_set_pipapo: allocate pcpu scratch maps on clone
ppp: ensure minimum packet size in ppp_write()
rocker: fix a sleeping in atomic bug
staging: greybus: audio: Check null pointer
fsl/fman: Check for null pointer after calling devm_ioremap
Bluetooth: hci_bcm: Check for error irq
Bluetooth: hci_qca: Fix NULL vs IS_ERR_OR_NULL check in qca_serdev_probe
usb: dwc3: qcom: Fix NULL vs IS_ERR checking in dwc3_qcom_probe
HID: hid-uclogic-params: Invalid parameter check in uclogic_params_init
HID: hid-uclogic-params: Invalid parameter check in uclogic_params_get_str_desc
HID: hid-uclogic-params: Invalid parameter check in uclogic_params_huion_init
HID: hid-uclogic-params: Invalid parameter check in uclogic_params_frame_init_v1_buttonpad
debugfs: lockdown: Allow reading debugfs files that are not world readable
net/mlx5e: Fix page DMA map/unmap attributes
net/mlx5e: Don't block routes with nexthop objects in SW
Revert "net/mlx5e: Block offload of outer header csum for UDP tunnels"
net/mlx5: Set command entry semaphore up once got index free
lib/mpi: Add the return value check of kcalloc()
Bluetooth: L2CAP: uninitialized variables in l2cap_sock_setsockopt()
spi: spi-meson-spifc: Add missing pm_runtime_disable() in meson_spifc_probe
ax25: uninitialized variable in ax25_setsockopt()
netrom: fix api breakage in nr_setsockopt()
regmap: Call regmap_debugfs_exit() prior to _init()
can: mcp251xfd: add missing newline to printed strings
tpm: add request_locality before write TPM_INT_ENABLE
tpm_tis: Fix an error handling path in 'tpm_tis_core_init()'
can: softing: softing_startstop(): fix set but not used variable warning
can: xilinx_can: xcan_probe(): check for error irq
pcmcia: fix setting of kthread task states
iwlwifi: mvm: Use div_s64 instead of do_div in iwl_mvm_ftm_rtt_smoothing()
net: mcs7830: handle usb read errors properly
ext4: avoid trim error on fs with small groups
ALSA: jack: Add missing rwsem around snd_ctl_remove() calls
ALSA: PCM: Add missing rwsem around snd_ctl_remove() calls
ALSA: hda: Add missing rwsem around snd_ctl_remove() calls
RDMA/bnxt_re: Scan the whole bitmap when checking if "disabling RCFW with pending cmd-bit"
RDMA/hns: Validate the pkey index
scsi: pm80xx: Update WARN_ON check in pm8001_mpi_build_cmd()
clk: imx8mn: Fix imx8mn_clko1_sels
powerpc/prom_init: Fix improper check of prom_getprop()
ASoC: uniphier: drop selecting non-existing SND_SOC_UNIPHIER_AIO_DMA
dt-bindings: thermal: Fix definition of cooling-maps contribution property
powerpc/64s: Convert some cpu_setup() and cpu_restore() functions to C
powerpc/perf: MMCR0 control for PMU registers under PMCC=00
powerpc/perf: move perf irq/nmi handling details into traps.c
powerpc/irq: Add helper to set regs->softe
powerpc/perf: Fix PMU callbacks to clear pending PMI before resetting an overflown PMC
powerpc/32s: Fix shift-out-of-bounds in KASAN init
clocksource: Reduce clocksource-skew threshold
clocksource: Avoid accidental unstable marking of clocksources
ALSA: oss: fix compile error when OSS_DEBUG is enabled
ALSA: usb-audio: Drop superfluous '0' in Presonus Studio 1810c's ID
char/mwave: Adjust io port register size
binder: fix handling of error during copy
openrisc: Add clone3 ABI wrapper
iommu/io-pgtable-arm: Fix table descriptor paddr formatting
scsi: ufs: Fix race conditions related to driver data
RDMA/qedr: Fix reporting max_{send/recv}_wr attrs
PCI/MSI: Fix pci_irq_vector()/pci_irq_get_affinity()
powerpc/powermac: Add additional missing lockdep_register_key()
RDMA/core: Let ib_find_gid() continue search even after empty entry
RDMA/cma: Let cma_resolve_ib_dev() continue search even after empty entry
ASoC: rt5663: Handle device_property_read_u32_array error codes
of: unittest: fix warning on PowerPC frame size warning
of: unittest: 64 bit dma address test requires arch support
clk: stm32: Fix ltdc's clock turn off by clk_disable_unused() after system enter shell
mips: add SYS_HAS_CPU_MIPS64_R5 config for MIPS Release 5 support
mips: fix Kconfig reference to PHYS_ADDR_T_64BIT
dmaengine: pxa/mmp: stop referencing config->slave_id
iommu/amd: Remove iommu_init_ga()
iommu/amd: Restore GA log/tail pointer on host resume
ASoC: Intel: catpt: Test dmaengine_submit() result before moving on
iommu/iova: Fix race between FQ timeout and teardown
scsi: block: pm: Always set request queue runtime active in blk_post_runtime_resume()
phy: uniphier-usb3ss: fix unintended writing zeros to PHY register
ASoC: mediatek: Check for error clk pointer
ASoC: samsung: idma: Check of ioremap return value
misc: lattice-ecp3-config: Fix task hung when firmware load failed
counter: stm32-lptimer-cnt: remove iio counter abi
arm64: tegra: Fix Tegra194 HDA {clock,reset}-names ordering
arm64: tegra: Remove non existent Tegra194 reset
mips: lantiq: add support for clk_set_parent()
mips: bcm63xx: add support for clk_set_parent()
powerpc/xive: Add missing null check after calling kmalloc
ASoC: fsl_mqs: fix MODULE_ALIAS
RDMA/cxgb4: Set queue pair state when being queried
ASoC: fsl_asrc: refine the check of available clock divider
clk: bm1880: remove kfrees on static allocations
of: base: Fix phandle argument length mismatch error message
ARM: dts: omap3-n900: Fix lp5523 for multi color
Bluetooth: Fix debugfs entry leak in hci_register_dev()
fs: dlm: filter user dlm messages for kernel locks
drm/lima: fix warning when CONFIG_DEBUG_SG=y & CONFIG_DMA_API_DEBUG=y
selftests/bpf: Fix bpf_object leak in skb_ctx selftest
ar5523: Fix null-ptr-deref with unexpected WDCMSG_TARGET_START reply
drm/bridge: dw-hdmi: handle ELD when DRM_BRIDGE_ATTACH_NO_CONNECTOR
drm/nouveau/pmu/gm200-: avoid touching PMU outside of DEVINIT/PREOS/ACR
media: atomisp: fix try_fmt logic
media: atomisp: set per-device's default mode
media: atomisp-ov2680: Fix ov2680_set_fmt() clobbering the exposure
ARM: shmobile: rcar-gen2: Add missing of_node_put()
batman-adv: allow netlink usage in unprivileged containers
media: atomisp: handle errors at sh_css_create_isp_params()
ath11k: Fix crash caused by uninitialized TX ring
usb: gadget: f_fs: Use stream_open() for endpoint files
drm: panel-orientation-quirks: Add quirk for the Lenovo Yoga Book X91F/L
HID: apple: Do not reset quirks when the Fn key is not found
media: b2c2: Add missing check in flexcop_pci_isr:
EDAC/synopsys: Use the quirk for version instead of ddr version
ARM: imx: rename DEBUG_IMX21_IMX27_UART to DEBUG_IMX27_UART
drm/amd/display: check top_pipe_to_program pointer
drm/amdgpu/display: set vblank_disable_immediate for DC
soc: ti: pruss: fix referenced node in error message
mlxsw: pci: Add shutdown method in PCI driver
drm/bridge: megachips: Ensure both bridges are probed before registration
tty: serial: imx: disable UCR4_OREN in .stop_rx() instead of .shutdown()
gpiolib: acpi: Do not set the IRQ type if the IRQ is already in use
HSI: core: Fix return freed object in hsi_new_client
crypto: jitter - consider 32 LSB for APT
mwifiex: Fix skb_over_panic in mwifiex_usb_recv()
rsi: Fix use-after-free in rsi_rx_done_handler()
rsi: Fix out-of-bounds read in rsi_read_pkt()
ath11k: Avoid NULL ptr access during mgmt tx cleanup
media: venus: avoid calling core_clk_setrate() concurrently during concurrent video sessions
ACPI / x86: Drop PWM2 device on Lenovo Yoga Book from always present table
ACPI: Change acpi_device_always_present() into acpi_device_override_status()
ACPI / x86: Allow specifying acpi_device_override_status() quirks by path
ACPI / x86: Add not-present quirk for the PCI0.SDHB.BRC1 device on the GPD win
arm64: dts: ti: j7200-main: Fix 'dtbs_check' serdes_ln_ctrl node
usb: uhci: add aspeed ast2600 uhci support
floppy: Add max size check for user space request
x86/mm: Flush global TLB when switching to trampoline page-table
drm: rcar-du: Fix CRTC timings when CMM is used
media: uvcvideo: Increase UVC_CTRL_CONTROL_TIMEOUT to 5 seconds.
media: rcar-vin: Update format alignment constraints
media: saa7146: hexium_orion: Fix a NULL pointer dereference in hexium_attach()
media: m920x: don't use stack on USB reads
thunderbolt: Runtime PM activate both ends of the device link
iwlwifi: mvm: synchronize with FW after multicast commands
iwlwifi: mvm: avoid clearing a just saved session protection id
ath11k: avoid deadlock by change ieee80211_queue_work for regd_update_work
ath10k: Fix tx hanging
net-sysfs: update the queue counts in the unregistration path
net: phy: prefer 1000baseT over 1000baseKX
gpio: aspeed: Convert aspeed_gpio.lock to raw_spinlock
selftests/ftrace: make kprobe profile testcase description unique
ath11k: Avoid false DEADLOCK warning reported by lockdep
x86/mce: Allow instrumentation during task work queueing
x86/mce: Mark mce_panic() noinstr
x86/mce: Mark mce_end() noinstr
x86/mce: Mark mce_read_aux() noinstr
net: bonding: debug: avoid printing debug logs when bond is not notifying peers
bpf: Do not WARN in bpf_warn_invalid_xdp_action()
HID: quirks: Allow inverting the absolute X/Y values
media: igorplugusb: receiver overflow should be reported
media: saa7146: hexium_gemini: Fix a NULL pointer dereference in hexium_attach()
mmc: core: Fixup storing of OCR for MMC_QUIRK_NONSTD_SDIO
audit: ensure userspace is penalized the same as the kernel when under pressure
arm64: dts: ls1028a-qds: move rtc node to the correct i2c bus
arm64: tegra: Adjust length of CCPLEX cluster MMIO region
PM: runtime: Add safety net to supplier device release
cpufreq: Fix initialization of min and max frequency QoS requests
usb: hub: Add delay for SuperSpeed hub resume to let links transit to U0
ath9k: Fix out-of-bound memcpy in ath9k_hif_usb_rx_stream
rtw88: 8822c: update rx settings to prevent potential hw deadlock
PM: AVS: qcom-cpr: Use div64_ul instead of do_div
iwlwifi: fix leaks/bad data after failed firmware load
iwlwifi: remove module loading failure message
iwlwifi: mvm: Fix calculation of frame length
iwlwifi: pcie: make sure prph_info is set when treating wakeup IRQ
um: registers: Rename function names to avoid conflicts and build problems
ath11k: Fix napi related hang
Bluetooth: vhci: Set HCI_QUIRK_VALID_LE_STATES
xfrm: rate limit SA mapping change message to user space
drm/etnaviv: consider completed fence seqno in hang check
jffs2: GC deadlock reading a page that is used in jffs2_write_begin()
ACPICA: actypes.h: Expand the ACPI_ACCESS_ definitions
ACPICA: Utilities: Avoid deleting the same object twice in a row
ACPICA: Executer: Fix the REFCLASS_REFOF case in acpi_ex_opcode_1A_0T_1R()
ACPICA: Fix wrong interpretation of PCC address
ACPICA: Hardware: Do not flush CPU cache when entering S4 and S5
drm/amdgpu: fixup bad vram size on gmc v8
amdgpu/pm: Make sysfs pm attributes as read-only for VFs
ACPI: battery: Add the ThinkPad "Not Charging" quirk
btrfs: remove BUG_ON() in find_parent_nodes()
btrfs: remove BUG_ON(!eie) in find_parent_nodes
net: mdio: Demote probed message to debug print
mac80211: allow non-standard VHT MCS-10/11
dm btree: add a defensive bounds check to insert_at()
dm space map common: add bounds check to sm_ll_lookup_bitmap()
mlxsw: pci: Avoid flow control for EMAD packets
net: phy: marvell: configure RGMII delays for 88E1118
net: gemini: allow any RGMII interface mode
regulator: qcom_smd: Align probe function with rpmh-regulator
serial: pl010: Drop CR register reset on set_termios
serial: core: Keep mctrl register state and cached copy in sync
random: do not throw away excess input to crng_fast_load
parisc: Avoid calling faulthandler_disabled() twice
x86/kbuild: Enable CONFIG_KALLSYMS_ALL=y in the defconfigs
powerpc/6xx: add missing of_node_put
powerpc/powernv: add missing of_node_put
powerpc/cell: add missing of_node_put
powerpc/btext: add missing of_node_put
powerpc/watchdog: Fix missed watchdog reset due to memory ordering race
i2c: i801: Don't silently correct invalid transfer size
powerpc/smp: Move setup_profiling_timer() under CONFIG_PROFILING
i2c: mpc: Correct I2C reset procedure
clk: meson: gxbb: Fix the SDM_EN bit for MPLL0 on GXBB
powerpc/powermac: Add missing lockdep_register_key()
KVM: PPC: Book3S: Suppress warnings when allocating too big memory slots
KVM: PPC: Book3S: Suppress failed alloc warning in H_COPY_TOFROM_GUEST
w1: Misuse of get_user()/put_user() reported by sparse
nvmem: core: set size for sysfs bin file
dm: fix alloc_dax error handling in alloc_dev
scsi: lpfc: Trigger SLI4 firmware dump before doing driver cleanup
ALSA: seq: Set upper limit of processed events
MIPS: Loongson64: Use three arguments for slti
powerpc/40x: Map 32Mbytes of memory at startup
selftests/powerpc/spectre_v2: Return skip code when miss_percent is high
powerpc: handle kdump appropriately with crash_kexec_post_notifiers option
powerpc/fadump: Fix inaccurate CPU state info in vmcore generated with panic
udf: Fix error handling in udf_new_inode()
MIPS: OCTEON: add put_device() after of_find_device_by_node()
irqchip/gic-v4: Disable redistributors' view of the VPE table at boot time
i2c: designware-pci: Fix to change data types of hcnt and lcnt parameters
MIPS: Octeon: Fix build errors using clang
scsi: sr: Don't use GFP_DMA
ASoC: mediatek: mt8173: fix device_node leak
ASoC: mediatek: mt8183: fix device_node leak
phy: mediatek: Fix missing check in mtk_mipi_tx_probe
rpmsg: core: Clean up resources on announce_create failure.
crypto: omap-aes - Fix broken pm_runtime_and_get() usage
crypto: stm32/crc32 - Fix kernel BUG triggered in probe()
crypto: caam - replace this_cpu_ptr with raw_cpu_ptr
ubifs: Error path in ubifs_remount_rw() seems to wrongly free write buffers
tpm: fix NPE on probe for missing device
spi: uniphier: Fix a bug that doesn't point to private data correctly
xen/gntdev: fix unmap notification order
fuse: Pass correct lend value to filemap_write_and_wait_range()
serial: Fix incorrect rs485 polarity on uart open
cputime, cpuacct: Include guest time in user time in cpuacct.stat
tracing/kprobes: 'nmissed' not showed correctly for kretprobe
iwlwifi: mvm: Increase the scan timeout guard to 30 seconds
s390/mm: fix 2KB pgtable release race
device property: Fix fwnode_graph_devcon_match() fwnode leak
drm/etnaviv: limit submit sizes
drm/nouveau/kms/nv04: use vzalloc for nv04_display
drm/bridge: analogix_dp: Make PSR-exit block less
parisc: Fix lpa and lpa_user defines
powerpc/64s/radix: Fix huge vmap false positive
PCI: xgene: Fix IB window setup
PCI: pciehp: Use down_read/write_nested(reset_lock) to fix lockdep errors
PCI: pci-bridge-emul: Make expansion ROM Base Address register read-only
PCI: pci-bridge-emul: Properly mark reserved PCIe bits in PCI config space
PCI: pci-bridge-emul: Fix definitions of reserved bits
PCI: pci-bridge-emul: Correctly set PCIe capabilities
PCI: pci-bridge-emul: Set PCI_STATUS_CAP_LIST for PCIe device
xfrm: fix policy lookup for ipv6 gre packets
btrfs: fix deadlock between quota enable and other quota operations
btrfs: check the root node for uptodate before returning it
btrfs: respect the max size in the header when activating swap file
ext4: make sure to reset inode lockdep class when quota enabling fails
ext4: make sure quota gets properly shutdown on error
ext4: fix a possible ABBA deadlock due to busy PA
ext4: initialize err_blk before calling __ext4_get_inode_loc
ext4: fix fast commit may miss tracking range for FALLOC_FL_ZERO_RANGE
ext4: set csum seed in tmp inode while migrating to extents
ext4: Fix BUG_ON in ext4_bread when write quota data
ext4: use ext4_ext_remove_space() for fast commit replay delete range
ext4: fast commit may miss tracking unwritten range during ftruncate
ext4: destroy ext4_fc_dentry_cachep kmemcache on module removal
ext4: fix null-ptr-deref in '__ext4_journal_ensure_credits'
ext4: don't use the orphan list when migrating an inode
drm/radeon: fix error handling in radeon_driver_open_kms
of: base: Improve argument length mismatch error
firmware: Update Kconfig help text for Google firmware
can: mcp251xfd: mcp251xfd_tef_obj_read(): fix typo in error message
media: rcar-csi2: Optimize the selection PHTW register
drm/vc4: hdmi: Make sure the device is powered with CEC
media: correct MEDIA_TEST_SUPPORT help text
Documentation: dmaengine: Correctly describe dmatest with channel unset
Documentation: ACPI: Fix data node reference documentation
Documentation: refer to config RANDOMIZE_BASE for kernel address-space randomization
Documentation: fix firewire.rst ABI file path error
Bluetooth: hci_sync: Fix not setting adv set duration
scsi: core: Show SCMD_LAST in text form
dmaengine: uniphier-xdmac: Fix type of address variables
RDMA/hns: Modify the mapping attribute of doorbell to device
RDMA/rxe: Fix a typo in opcode name
dmaengine: stm32-mdma: fix STM32_MDMA_CTBR_TSEL_MASK
Revert "net/mlx5: Add retry mechanism to the command entry index allocation"
powerpc/cell: Fix clang -Wimplicit-fallthrough warning
powerpc/fsl/dts: Enable WA for erratum A-009885 on fman3l MDIO buses
block: Fix fsync always failed if once failed
bpftool: Remove inclusion of utilities.mak from Makefiles
xdp: check prog type before updating BPF link
perf evsel: Override attr->sample_period for non-libpfm4 events
ipv4: update fib_info_cnt under spinlock protection
ipv4: avoid quadratic behavior in netns dismantle
net/fsl: xgmac_mdio: Add workaround for erratum A-009885
net/fsl: xgmac_mdio: Fix incorrect iounmap when removing module
parisc: pdc_stable: Fix memory leak in pdcs_register_pathentries
f2fs: compress: fix potential deadlock of compress file
f2fs: fix to reserve space for IO align feature
af_unix: annote lockless accesses to unix_tot_inflight & gc_in_progress
clk: Emit a stern warning with writable debugfs enabled
clk: si5341: Fix clock HW provider cleanup
net/smc: Fix hung_task when removing SMC-R devices
net: axienet: increase reset timeout
net: axienet: Wait for PhyRstCmplt after core reset
net: axienet: reset core on initialization prior to MDIO access
net: axienet: add missing memory barriers
net: axienet: limit minimum TX ring size
net: axienet: Fix TX ring slot available check
net: axienet: fix number of TX ring slots for available check
net: axienet: fix for TX busy handling
net: axienet: increase default TX ring size to 128
HID: vivaldi: fix handling devices not using numbered reports
rtc: pxa: fix null pointer dereference
vdpa/mlx5: Fix wrong configuration of virtio_version_1_0
virtio_ring: mark ring unused on error
taskstats: Cleanup the use of task->exit_code
inet: frags: annotate races around fqdir->dead and fqdir->high_thresh
netns: add schedule point in ops_exit_list()
xfrm: Don't accidentally set RTO_ONLINK in decode_session4()
gre: Don't accidentally set RTO_ONLINK in gre_fill_metadata_dst()
libcxgb: Don't accidentally set RTO_ONLINK in cxgb_find_route()
perf script: Fix hex dump character output
dmaengine: at_xdmac: Don't start transactions at tx_submit level
dmaengine: at_xdmac: Start transfer for cyclic channels in issue_pending
dmaengine: at_xdmac: Print debug message after realeasing the lock
dmaengine: at_xdmac: Fix concurrency over xfers_list
dmaengine: at_xdmac: Fix lld view setting
dmaengine: at_xdmac: Fix at_xdmac_lld struct definition
perf probe: Fix ppc64 'perf probe add events failed' case
devlink: Remove misleading internal_flags from health reporter dump
arm64: dts: qcom: msm8996: drop not documented adreno properties
net: bonding: fix bond_xmit_broadcast return value error bug
net_sched: restore "mpu xxx" handling
bcmgenet: add WOL IRQ check
net: ethernet: mtk_eth_soc: fix error checking in mtk_mac_config()
net: sfp: fix high power modules without diagnostic monitoring
net: mscc: ocelot: fix using match before it is set
dt-bindings: display: meson-dw-hdmi: add missing sound-name-prefix property
dt-bindings: display: meson-vpu: Add missing amlogic,canvas property
dt-bindings: watchdog: Require samsung,syscon-phandle for Exynos7
scripts/dtc: dtx_diff: remove broken example from help text
lib82596: Fix IRQ check in sni_82596_probe
mm/hmm.c: allow VM_MIXEDMAP to work with hmm_range_fault
lib/test_meminit: destroy cache in kmem_cache_alloc_bulk() test
mtd: nand: bbt: Fix corner case in bad block table handling
ath10k: Fix the MTU size on QCA9377 SDIO
scripts: sphinx-pre-install: add required ctex dependency
scripts: sphinx-pre-install: Fix ctex support on Debian
Linux 5.10.94
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I857f2417c899508815a1ba13d1285fd400a1f133
1328 lines
35 KiB
C
1328 lines
35 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* CPU-agnostic ARM page table allocator.
|
|
*
|
|
* Copyright (C) 2014 ARM Limited
|
|
*
|
|
* Author: Will Deacon <will.deacon@arm.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/io-pgtable.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <asm/barrier.h>
|
|
|
|
#include "io-pgtable-arm.h"
|
|
|
|
#define ARM_LPAE_MAX_ADDR_BITS 52
|
|
#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16
|
|
#define ARM_LPAE_MAX_LEVELS 4
|
|
|
|
/* Struct accessors */
|
|
#define io_pgtable_to_data(x) \
|
|
container_of((x), struct arm_lpae_io_pgtable, iop)
|
|
|
|
#define io_pgtable_ops_to_data(x) \
|
|
io_pgtable_to_data(io_pgtable_ops_to_pgtable(x))
|
|
|
|
/*
|
|
* Calculate the right shift amount to get to the portion describing level l
|
|
* in a virtual address mapped by the pagetable in d.
|
|
*/
|
|
#define ARM_LPAE_LVL_SHIFT(l,d) \
|
|
(((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level) + \
|
|
ilog2(sizeof(arm_lpae_iopte)))
|
|
|
|
#define ARM_LPAE_GRANULE(d) \
|
|
(sizeof(arm_lpae_iopte) << (d)->bits_per_level)
|
|
#define ARM_LPAE_PGD_SIZE(d) \
|
|
(sizeof(arm_lpae_iopte) << (d)->pgd_bits)
|
|
|
|
#define ARM_LPAE_PTES_PER_TABLE(d) \
|
|
(ARM_LPAE_GRANULE(d) >> ilog2(sizeof(arm_lpae_iopte)))
|
|
|
|
/*
|
|
* Calculate the index at level l used to map virtual address a using the
|
|
* pagetable in d.
|
|
*/
|
|
#define ARM_LPAE_PGD_IDX(l,d) \
|
|
((l) == (d)->start_level ? (d)->pgd_bits - (d)->bits_per_level : 0)
|
|
|
|
#define ARM_LPAE_LVL_IDX(a,l,d) \
|
|
(((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \
|
|
((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1))
|
|
|
|
/* Calculate the block/page mapping size at level l for pagetable in d. */
|
|
#define ARM_LPAE_BLOCK_SIZE(l,d) (1ULL << ARM_LPAE_LVL_SHIFT(l,d))
|
|
|
|
/* Page table bits */
|
|
#define ARM_LPAE_PTE_TYPE_SHIFT 0
|
|
#define ARM_LPAE_PTE_TYPE_MASK 0x3
|
|
|
|
#define ARM_LPAE_PTE_TYPE_BLOCK 1
|
|
#define ARM_LPAE_PTE_TYPE_TABLE 3
|
|
#define ARM_LPAE_PTE_TYPE_PAGE 3
|
|
|
|
#define ARM_LPAE_PTE_ADDR_MASK GENMASK_ULL(47,12)
|
|
|
|
#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63)
|
|
#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53)
|
|
#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10)
|
|
#define ARM_LPAE_PTE_SH_NS (((arm_lpae_iopte)0) << 8)
|
|
#define ARM_LPAE_PTE_SH_OS (((arm_lpae_iopte)2) << 8)
|
|
#define ARM_LPAE_PTE_SH_IS (((arm_lpae_iopte)3) << 8)
|
|
#define ARM_LPAE_PTE_NS (((arm_lpae_iopte)1) << 5)
|
|
#define ARM_LPAE_PTE_VALID (((arm_lpae_iopte)1) << 0)
|
|
|
|
#define ARM_LPAE_PTE_ATTR_LO_MASK (((arm_lpae_iopte)0x3ff) << 2)
|
|
/* Ignore the contiguous bit for block splitting */
|
|
#define ARM_LPAE_PTE_ATTR_HI_MASK (((arm_lpae_iopte)6) << 52)
|
|
#define ARM_LPAE_PTE_ATTR_MASK (ARM_LPAE_PTE_ATTR_LO_MASK | \
|
|
ARM_LPAE_PTE_ATTR_HI_MASK)
|
|
/* Software bit for solving coherency races */
|
|
#define ARM_LPAE_PTE_SW_SYNC (((arm_lpae_iopte)1) << 55)
|
|
|
|
/* Stage-1 PTE */
|
|
#define ARM_LPAE_PTE_AP_UNPRIV (((arm_lpae_iopte)1) << 6)
|
|
#define ARM_LPAE_PTE_AP_RDONLY (((arm_lpae_iopte)2) << 6)
|
|
#define ARM_LPAE_PTE_ATTRINDX_SHIFT 2
|
|
#define ARM_LPAE_PTE_nG (((arm_lpae_iopte)1) << 11)
|
|
|
|
/* Stage-2 PTE */
|
|
#define ARM_LPAE_PTE_HAP_FAULT (((arm_lpae_iopte)0) << 6)
|
|
#define ARM_LPAE_PTE_HAP_READ (((arm_lpae_iopte)1) << 6)
|
|
#define ARM_LPAE_PTE_HAP_WRITE (((arm_lpae_iopte)2) << 6)
|
|
#define ARM_LPAE_PTE_MEMATTR_OIWB (((arm_lpae_iopte)0xf) << 2)
|
|
#define ARM_LPAE_PTE_MEMATTR_NC (((arm_lpae_iopte)0x5) << 2)
|
|
#define ARM_LPAE_PTE_MEMATTR_DEV (((arm_lpae_iopte)0x1) << 2)
|
|
|
|
/* Register bits */
|
|
#define ARM_LPAE_VTCR_SL0_MASK 0x3
|
|
|
|
#define ARM_LPAE_TCR_T0SZ_SHIFT 0
|
|
|
|
#define ARM_LPAE_VTCR_PS_SHIFT 16
|
|
#define ARM_LPAE_VTCR_PS_MASK 0x7
|
|
|
|
#define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3)
|
|
#define ARM_LPAE_MAIR_ATTR_MASK 0xff
|
|
#define ARM_LPAE_MAIR_ATTR_DEVICE 0x04ULL
|
|
#define ARM_LPAE_MAIR_ATTR_NC 0x44ULL
|
|
#define ARM_LPAE_MAIR_ATTR_INC_OWBRANWA 0xe4ULL
|
|
#define ARM_LPAE_MAIR_ATTR_IWBRWA_OWBRANWA 0xefULL
|
|
#define ARM_LPAE_MAIR_ATTR_INC_OWBRWA 0xf4ULL
|
|
#define ARM_LPAE_MAIR_ATTR_WBRWA 0xffULL
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_NC 0
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_DEV 2
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE 3
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE_NWA 4
|
|
#define ARM_LPAE_MAIR_ATTR_IDX_ICACHE_OCACHE_NWA 5
|
|
|
|
#define ARM_MALI_LPAE_TTBR_ADRMODE_TABLE (3u << 0)
|
|
#define ARM_MALI_LPAE_TTBR_READ_INNER BIT(2)
|
|
#define ARM_MALI_LPAE_TTBR_SHARE_OUTER BIT(4)
|
|
|
|
#define ARM_MALI_LPAE_MEMATTR_IMP_DEF 0x88ULL
|
|
#define ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC 0x8DULL
|
|
|
|
/* IOPTE accessors */
|
|
#define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d))
|
|
|
|
#define iopte_type(pte,l) \
|
|
(((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK)
|
|
|
|
#define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK)
|
|
|
|
struct arm_lpae_io_pgtable {
|
|
struct io_pgtable iop;
|
|
|
|
int pgd_bits;
|
|
int start_level;
|
|
int bits_per_level;
|
|
|
|
void *pgd;
|
|
};
|
|
|
|
typedef u64 arm_lpae_iopte;
|
|
|
|
static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl,
|
|
enum io_pgtable_fmt fmt)
|
|
{
|
|
if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE)
|
|
return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE;
|
|
|
|
return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK;
|
|
}
|
|
|
|
static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr,
|
|
struct arm_lpae_io_pgtable *data)
|
|
{
|
|
arm_lpae_iopte pte = paddr;
|
|
|
|
/* Of the bits which overlap, either 51:48 or 15:12 are always RES0 */
|
|
return (pte | (pte >> (48 - 12))) & ARM_LPAE_PTE_ADDR_MASK;
|
|
}
|
|
|
|
static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
|
|
struct arm_lpae_io_pgtable *data)
|
|
{
|
|
u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK;
|
|
|
|
if (ARM_LPAE_GRANULE(data) < SZ_64K)
|
|
return paddr;
|
|
|
|
/* Rotate the packed high-order bits back to the top */
|
|
return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
|
|
}
|
|
|
|
static bool selftest_running = false;
|
|
|
|
static dma_addr_t __arm_lpae_dma_addr(void *pages)
|
|
{
|
|
return (dma_addr_t)virt_to_phys(pages);
|
|
}
|
|
|
|
static void *__arm_lpae_alloc_pages(size_t size, gfp_t gfp,
|
|
struct io_pgtable_cfg *cfg)
|
|
{
|
|
struct device *dev = cfg->iommu_dev;
|
|
int order = get_order(size);
|
|
struct page *p;
|
|
dma_addr_t dma;
|
|
void *pages;
|
|
|
|
VM_BUG_ON((gfp & __GFP_HIGHMEM));
|
|
p = alloc_pages_node(dev ? dev_to_node(dev) : NUMA_NO_NODE,
|
|
gfp | __GFP_ZERO, order);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
pages = page_address(p);
|
|
if (!cfg->coherent_walk) {
|
|
dma = dma_map_single(dev, pages, size, DMA_TO_DEVICE);
|
|
if (dma_mapping_error(dev, dma))
|
|
goto out_free;
|
|
/*
|
|
* We depend on the IOMMU being able to work with any physical
|
|
* address directly, so if the DMA layer suggests otherwise by
|
|
* translating or truncating them, that bodes very badly...
|
|
*/
|
|
if (dma != virt_to_phys(pages))
|
|
goto out_unmap;
|
|
}
|
|
|
|
return pages;
|
|
|
|
out_unmap:
|
|
dev_err(dev, "Cannot accommodate DMA translation for IOMMU page tables\n");
|
|
dma_unmap_single(dev, dma, size, DMA_TO_DEVICE);
|
|
out_free:
|
|
__free_pages(p, order);
|
|
return NULL;
|
|
}
|
|
|
|
static void __arm_lpae_free_pages(void *pages, size_t size,
|
|
struct io_pgtable_cfg *cfg)
|
|
{
|
|
if (!cfg->coherent_walk)
|
|
dma_unmap_single(cfg->iommu_dev, __arm_lpae_dma_addr(pages),
|
|
size, DMA_TO_DEVICE);
|
|
free_pages((unsigned long)pages, get_order(size));
|
|
}
|
|
|
|
static void __arm_lpae_sync_pte(arm_lpae_iopte *ptep, int num_entries,
|
|
struct io_pgtable_cfg *cfg)
|
|
{
|
|
dma_sync_single_for_device(cfg->iommu_dev, __arm_lpae_dma_addr(ptep),
|
|
sizeof(*ptep) * num_entries, DMA_TO_DEVICE);
|
|
}
|
|
|
|
static void __arm_lpae_clear_pte(arm_lpae_iopte *ptep, struct io_pgtable_cfg *cfg)
|
|
{
|
|
|
|
*ptep = 0;
|
|
|
|
if (!cfg->coherent_walk)
|
|
__arm_lpae_sync_pte(ptep, 1, cfg);
|
|
}
|
|
|
|
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|
struct iommu_iotlb_gather *gather,
|
|
unsigned long iova, size_t size, size_t pgcount,
|
|
int lvl, arm_lpae_iopte *ptep);
|
|
|
|
static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|
phys_addr_t paddr, arm_lpae_iopte prot,
|
|
int lvl, int num_entries, arm_lpae_iopte *ptep)
|
|
{
|
|
arm_lpae_iopte pte = prot;
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
|
int i;
|
|
|
|
if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1)
|
|
pte |= ARM_LPAE_PTE_TYPE_PAGE;
|
|
else
|
|
pte |= ARM_LPAE_PTE_TYPE_BLOCK;
|
|
|
|
for (i = 0; i < num_entries; i++)
|
|
ptep[i] = pte | paddr_to_iopte(paddr + i * sz, data);
|
|
|
|
if (!cfg->coherent_walk)
|
|
__arm_lpae_sync_pte(ptep, num_entries, cfg);
|
|
}
|
|
|
|
static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|
unsigned long iova, phys_addr_t paddr,
|
|
arm_lpae_iopte prot, int lvl, int num_entries,
|
|
arm_lpae_iopte *ptep)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_entries; i++)
|
|
if (iopte_leaf(ptep[i], lvl, data->iop.fmt)) {
|
|
/* We require an unmap first */
|
|
WARN_ON(!selftest_running);
|
|
return -EEXIST;
|
|
} else if (iopte_type(ptep[i], lvl) == ARM_LPAE_PTE_TYPE_TABLE) {
|
|
/*
|
|
* We need to unmap and free the old table before
|
|
* overwriting it with a block entry.
|
|
*/
|
|
arm_lpae_iopte *tblp;
|
|
size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
|
|
|
tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
|
|
if (__arm_lpae_unmap(data, NULL, iova + i * sz, sz, 1,
|
|
lvl, tblp) != sz) {
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
__arm_lpae_init_pte(data, paddr, prot, lvl, num_entries, ptep);
|
|
return 0;
|
|
}
|
|
|
|
static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table,
|
|
arm_lpae_iopte *ptep,
|
|
arm_lpae_iopte curr,
|
|
struct arm_lpae_io_pgtable *data)
|
|
{
|
|
arm_lpae_iopte old, new;
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
|
|
new = paddr_to_iopte(__pa(table), data) | ARM_LPAE_PTE_TYPE_TABLE;
|
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
|
new |= ARM_LPAE_PTE_NSTABLE;
|
|
|
|
/*
|
|
* Ensure the table itself is visible before its PTE can be.
|
|
* Whilst we could get away with cmpxchg64_release below, this
|
|
* doesn't have any ordering semantics when !CONFIG_SMP.
|
|
*/
|
|
dma_wmb();
|
|
|
|
old = cmpxchg64_relaxed(ptep, curr, new);
|
|
|
|
if (cfg->coherent_walk || (old & ARM_LPAE_PTE_SW_SYNC))
|
|
return old;
|
|
|
|
/* Even if it's not ours, there's no point waiting; just kick it */
|
|
__arm_lpae_sync_pte(ptep, 1, cfg);
|
|
if (old == curr)
|
|
WRITE_ONCE(*ptep, new | ARM_LPAE_PTE_SW_SYNC);
|
|
|
|
return old;
|
|
}
|
|
|
|
static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
|
phys_addr_t paddr, size_t size, size_t pgcount,
|
|
arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep,
|
|
gfp_t gfp, size_t *mapped)
|
|
{
|
|
arm_lpae_iopte *cptep, pte;
|
|
size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
|
size_t tblsz = ARM_LPAE_GRANULE(data);
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
int ret = 0, num_entries, max_entries, map_idx_start;
|
|
|
|
/* Find our entry at the current level */
|
|
map_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
|
|
ptep += map_idx_start;
|
|
|
|
/* If we can install a leaf entry at this level, then do so */
|
|
if (size == block_size) {
|
|
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - map_idx_start;
|
|
num_entries = min_t(int, pgcount, max_entries);
|
|
ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl, num_entries, ptep);
|
|
if (!ret && mapped)
|
|
*mapped += num_entries * size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* We can't allocate tables at the final level */
|
|
if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1))
|
|
return -EINVAL;
|
|
|
|
/* Grab a pointer to the next level */
|
|
pte = READ_ONCE(*ptep);
|
|
if (!pte) {
|
|
cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg);
|
|
if (!cptep)
|
|
return -ENOMEM;
|
|
|
|
pte = arm_lpae_install_table(cptep, ptep, 0, data);
|
|
if (pte)
|
|
__arm_lpae_free_pages(cptep, tblsz, cfg);
|
|
} else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) {
|
|
__arm_lpae_sync_pte(ptep, 1, cfg);
|
|
}
|
|
|
|
if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) {
|
|
cptep = iopte_deref(pte, data);
|
|
} else if (pte) {
|
|
/* We require an unmap first */
|
|
WARN_ON(!selftest_running);
|
|
return -EEXIST;
|
|
}
|
|
|
|
/* Rinse, repeat */
|
|
return __arm_lpae_map(data, iova, paddr, size, pgcount, prot, lvl + 1,
|
|
cptep, gfp, mapped);
|
|
}
|
|
|
|
static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
|
|
int prot)
|
|
{
|
|
arm_lpae_iopte pte;
|
|
|
|
if (data->iop.fmt == ARM_64_LPAE_S1 ||
|
|
data->iop.fmt == ARM_32_LPAE_S1) {
|
|
pte = ARM_LPAE_PTE_nG;
|
|
if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
|
|
pte |= ARM_LPAE_PTE_AP_RDONLY;
|
|
if (!(prot & IOMMU_PRIV))
|
|
pte |= ARM_LPAE_PTE_AP_UNPRIV;
|
|
} else {
|
|
pte = ARM_LPAE_PTE_HAP_FAULT;
|
|
if (prot & IOMMU_READ)
|
|
pte |= ARM_LPAE_PTE_HAP_READ;
|
|
if (prot & IOMMU_WRITE)
|
|
pte |= ARM_LPAE_PTE_HAP_WRITE;
|
|
}
|
|
|
|
/*
|
|
* Note that this logic is structured to accommodate Mali LPAE
|
|
* having stage-1-like attributes but stage-2-like permissions.
|
|
*/
|
|
if (data->iop.fmt == ARM_64_LPAE_S2 ||
|
|
data->iop.fmt == ARM_32_LPAE_S2) {
|
|
if (prot & IOMMU_MMIO)
|
|
pte |= ARM_LPAE_PTE_MEMATTR_DEV;
|
|
else if (prot & IOMMU_CACHE)
|
|
pte |= ARM_LPAE_PTE_MEMATTR_OIWB;
|
|
else
|
|
pte |= ARM_LPAE_PTE_MEMATTR_NC;
|
|
} else {
|
|
if (prot & IOMMU_MMIO)
|
|
pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV
|
|
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
|
else if ((prot & IOMMU_CACHE) && (prot & IOMMU_SYS_CACHE_NWA))
|
|
pte |= (ARM_LPAE_MAIR_ATTR_IDX_ICACHE_OCACHE_NWA
|
|
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
|
/* IOMMU_CACHE + IOMMU_SYS_CACHE equivalent to IOMMU_CACHE */
|
|
else if (prot & IOMMU_CACHE)
|
|
pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE
|
|
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
|
else if (prot & IOMMU_SYS_CACHE)
|
|
pte |= (ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE
|
|
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
|
else if (prot & IOMMU_SYS_CACHE_NWA)
|
|
pte |= (ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE_NWA
|
|
<< ARM_LPAE_PTE_ATTRINDX_SHIFT);
|
|
}
|
|
|
|
/*
|
|
* Also Mali has its own notions of shareability wherein its Inner
|
|
* domain covers the cores within the GPU, and its Outer domain is
|
|
* "outside the GPU" (i.e. either the Inner or System domain in CPU
|
|
* terms, depending on coherency).
|
|
*/
|
|
if (prot & IOMMU_CACHE && data->iop.fmt != ARM_MALI_LPAE)
|
|
pte |= ARM_LPAE_PTE_SH_IS;
|
|
else
|
|
pte |= ARM_LPAE_PTE_SH_OS;
|
|
|
|
if (prot & IOMMU_NOEXEC)
|
|
pte |= ARM_LPAE_PTE_XN;
|
|
|
|
if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
|
pte |= ARM_LPAE_PTE_NS;
|
|
|
|
if (data->iop.fmt != ARM_MALI_LPAE)
|
|
pte |= ARM_LPAE_PTE_AF;
|
|
|
|
return pte;
|
|
}
|
|
|
|
static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
|
|
phys_addr_t paddr, size_t pgsize, size_t pgcount,
|
|
int iommu_prot, gfp_t gfp, size_t *mapped)
|
|
{
|
|
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
arm_lpae_iopte *ptep = data->pgd;
|
|
int ret, lvl = data->start_level;
|
|
arm_lpae_iopte prot;
|
|
long iaext = (s64)iova >> cfg->ias;
|
|
|
|
/* If no access, then nothing to do */
|
|
if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
|
|
return 0;
|
|
|
|
if (WARN_ON(!pgsize || (pgsize & cfg->pgsize_bitmap) != pgsize))
|
|
return -EINVAL;
|
|
|
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)
|
|
iaext = ~iaext;
|
|
if (WARN_ON(iaext || paddr >> cfg->oas))
|
|
return -ERANGE;
|
|
|
|
prot = arm_lpae_prot_to_pte(data, iommu_prot);
|
|
ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
|
|
ptep, gfp, mapped);
|
|
/*
|
|
* Synchronise all PTE updates for the new mapping before there's
|
|
* a chance for anything to kick off a table walk for the new iova.
|
|
*/
|
|
wmb();
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
|
|
phys_addr_t paddr, size_t size, int iommu_prot, gfp_t gfp)
|
|
{
|
|
return arm_lpae_map_pages(ops, iova, paddr, size, 1, iommu_prot, gfp,
|
|
NULL);
|
|
}
|
|
|
|
static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
|
|
arm_lpae_iopte *ptep)
|
|
{
|
|
arm_lpae_iopte *start, *end;
|
|
unsigned long table_size;
|
|
|
|
if (lvl == data->start_level)
|
|
table_size = ARM_LPAE_PGD_SIZE(data);
|
|
else
|
|
table_size = ARM_LPAE_GRANULE(data);
|
|
|
|
start = ptep;
|
|
|
|
/* Only leaf entries at the last level */
|
|
if (lvl == ARM_LPAE_MAX_LEVELS - 1)
|
|
end = ptep;
|
|
else
|
|
end = (void *)ptep + table_size;
|
|
|
|
while (ptep != end) {
|
|
arm_lpae_iopte pte = *ptep++;
|
|
|
|
if (!pte || iopte_leaf(pte, lvl, data->iop.fmt))
|
|
continue;
|
|
|
|
__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
|
|
}
|
|
|
|
__arm_lpae_free_pages(start, table_size, &data->iop.cfg);
|
|
}
|
|
|
|
static void arm_lpae_free_pgtable(struct io_pgtable *iop)
|
|
{
|
|
struct arm_lpae_io_pgtable *data = io_pgtable_to_data(iop);
|
|
|
|
__arm_lpae_free_pgtable(data, data->start_level, data->pgd);
|
|
kfree(data);
|
|
}
|
|
|
|
static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
|
|
struct iommu_iotlb_gather *gather,
|
|
unsigned long iova, size_t size,
|
|
arm_lpae_iopte blk_pte, int lvl,
|
|
arm_lpae_iopte *ptep, size_t pgcount)
|
|
{
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
arm_lpae_iopte pte, *tablep;
|
|
phys_addr_t blk_paddr;
|
|
size_t tablesz = ARM_LPAE_GRANULE(data);
|
|
size_t split_sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
|
int ptes_per_table = ARM_LPAE_PTES_PER_TABLE(data);
|
|
int i, unmap_idx_start = -1, num_entries = 0, max_entries;
|
|
|
|
if (WARN_ON(lvl == ARM_LPAE_MAX_LEVELS))
|
|
return 0;
|
|
|
|
tablep = __arm_lpae_alloc_pages(tablesz, GFP_ATOMIC, cfg);
|
|
if (!tablep)
|
|
return 0; /* Bytes unmapped */
|
|
|
|
if (size == split_sz) {
|
|
unmap_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
|
|
max_entries = ptes_per_table - unmap_idx_start;
|
|
num_entries = min_t(int, pgcount, max_entries);
|
|
}
|
|
|
|
blk_paddr = iopte_to_paddr(blk_pte, data);
|
|
pte = iopte_prot(blk_pte);
|
|
|
|
for (i = 0; i < ptes_per_table; i++, blk_paddr += split_sz) {
|
|
/* Unmap! */
|
|
if (i >= unmap_idx_start && i < (unmap_idx_start + num_entries))
|
|
continue;
|
|
|
|
__arm_lpae_init_pte(data, blk_paddr, pte, lvl, 1, &tablep[i]);
|
|
}
|
|
|
|
pte = arm_lpae_install_table(tablep, ptep, blk_pte, data);
|
|
if (pte != blk_pte) {
|
|
__arm_lpae_free_pages(tablep, tablesz, cfg);
|
|
/*
|
|
* We may race against someone unmapping another part of this
|
|
* block, but anything else is invalid. We can't misinterpret
|
|
* a page entry here since we're never at the last level.
|
|
*/
|
|
if (iopte_type(pte, lvl - 1) != ARM_LPAE_PTE_TYPE_TABLE)
|
|
return 0;
|
|
|
|
tablep = iopte_deref(pte, data);
|
|
} else if (unmap_idx_start >= 0) {
|
|
for (i = 0; i < num_entries; i++)
|
|
io_pgtable_tlb_add_page(&data->iop, gather, iova + i * size, size);
|
|
|
|
return num_entries * size;
|
|
}
|
|
|
|
return __arm_lpae_unmap(data, gather, iova, size, pgcount, lvl, tablep);
|
|
}
|
|
|
|
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|
struct iommu_iotlb_gather *gather,
|
|
unsigned long iova, size_t size, size_t pgcount,
|
|
int lvl, arm_lpae_iopte *ptep)
|
|
{
|
|
arm_lpae_iopte pte;
|
|
struct io_pgtable *iop = &data->iop;
|
|
int i = 0, num_entries, max_entries, unmap_idx_start;
|
|
|
|
/* Something went horribly wrong and we ran out of page table */
|
|
if (WARN_ON(lvl == ARM_LPAE_MAX_LEVELS))
|
|
return 0;
|
|
|
|
unmap_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
|
|
ptep += unmap_idx_start;
|
|
pte = READ_ONCE(*ptep);
|
|
if (WARN_ON(!pte))
|
|
return 0;
|
|
|
|
/* If the size matches this level, we're in the right place */
|
|
if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
|
|
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - unmap_idx_start;
|
|
num_entries = min_t(int, pgcount, max_entries);
|
|
|
|
while (i < num_entries) {
|
|
pte = READ_ONCE(*ptep);
|
|
if (WARN_ON(!pte))
|
|
break;
|
|
|
|
__arm_lpae_clear_pte(ptep, &iop->cfg);
|
|
|
|
if (!iopte_leaf(pte, lvl, iop->fmt)) {
|
|
/* Also flush any partial walks */
|
|
io_pgtable_tlb_flush_walk(iop, iova + i * size, size,
|
|
ARM_LPAE_GRANULE(data));
|
|
__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
|
|
} else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) {
|
|
/*
|
|
* Order the PTE update against queueing the IOVA, to
|
|
* guarantee that a flush callback from a different CPU
|
|
* has observed it before the TLBIALL can be issued.
|
|
*/
|
|
smp_wmb();
|
|
} else {
|
|
io_pgtable_tlb_add_page(iop, gather, iova + i * size, size);
|
|
}
|
|
|
|
ptep++;
|
|
i++;
|
|
}
|
|
|
|
return i * size;
|
|
} else if (iopte_leaf(pte, lvl, iop->fmt)) {
|
|
/*
|
|
* Insert a table at the next level to map the old region,
|
|
* minus the part we want to unmap
|
|
*/
|
|
return arm_lpae_split_blk_unmap(data, gather, iova, size, pte,
|
|
lvl + 1, ptep, pgcount);
|
|
}
|
|
|
|
/* Keep on walkin' */
|
|
ptep = iopte_deref(pte, data);
|
|
return __arm_lpae_unmap(data, gather, iova, size, pgcount, lvl + 1, ptep);
|
|
}
|
|
|
|
static size_t arm_lpae_unmap_pages(struct io_pgtable_ops *ops, unsigned long iova,
|
|
size_t pgsize, size_t pgcount,
|
|
struct iommu_iotlb_gather *gather)
|
|
{
|
|
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
arm_lpae_iopte *ptep = data->pgd;
|
|
long iaext = (s64)iova >> cfg->ias;
|
|
|
|
if (WARN_ON(!pgsize || (pgsize & cfg->pgsize_bitmap) != pgsize || !pgcount))
|
|
return 0;
|
|
|
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)
|
|
iaext = ~iaext;
|
|
if (WARN_ON(iaext))
|
|
return 0;
|
|
|
|
return __arm_lpae_unmap(data, gather, iova, pgsize, pgcount,
|
|
data->start_level, ptep);
|
|
}
|
|
|
|
static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
|
size_t size, struct iommu_iotlb_gather *gather)
|
|
{
|
|
return arm_lpae_unmap_pages(ops, iova, size, 1, gather);
|
|
}
|
|
|
|
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
|
|
unsigned long iova)
|
|
{
|
|
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
|
arm_lpae_iopte pte, *ptep = data->pgd;
|
|
int lvl = data->start_level;
|
|
|
|
do {
|
|
/* Valid IOPTE pointer? */
|
|
if (!ptep)
|
|
return 0;
|
|
|
|
/* Grab the IOPTE we're interested in */
|
|
ptep += ARM_LPAE_LVL_IDX(iova, lvl, data);
|
|
pte = READ_ONCE(*ptep);
|
|
|
|
/* Valid entry? */
|
|
if (!pte)
|
|
return 0;
|
|
|
|
/* Leaf entry? */
|
|
if (iopte_leaf(pte, lvl, data->iop.fmt))
|
|
goto found_translation;
|
|
|
|
/* Take it to the next level */
|
|
ptep = iopte_deref(pte, data);
|
|
} while (++lvl < ARM_LPAE_MAX_LEVELS);
|
|
|
|
/* Ran out of page tables to walk */
|
|
return 0;
|
|
|
|
found_translation:
|
|
iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);
|
|
return iopte_to_paddr(pte, data) | iova;
|
|
}
|
|
|
|
static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)
|
|
{
|
|
unsigned long granule, page_sizes;
|
|
unsigned int max_addr_bits = 48;
|
|
|
|
/*
|
|
* We need to restrict the supported page sizes to match the
|
|
* translation regime for a particular granule. Aim to match
|
|
* the CPU page size if possible, otherwise prefer smaller sizes.
|
|
* While we're at it, restrict the block sizes to match the
|
|
* chosen granule.
|
|
*/
|
|
if (cfg->pgsize_bitmap & PAGE_SIZE)
|
|
granule = PAGE_SIZE;
|
|
else if (cfg->pgsize_bitmap & ~PAGE_MASK)
|
|
granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
|
|
else if (cfg->pgsize_bitmap & PAGE_MASK)
|
|
granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
|
|
else
|
|
granule = 0;
|
|
|
|
switch (granule) {
|
|
case SZ_4K:
|
|
page_sizes = (SZ_4K | SZ_2M | SZ_1G);
|
|
break;
|
|
case SZ_16K:
|
|
page_sizes = (SZ_16K | SZ_32M);
|
|
break;
|
|
case SZ_64K:
|
|
max_addr_bits = 52;
|
|
page_sizes = (SZ_64K | SZ_512M);
|
|
if (cfg->oas > 48)
|
|
page_sizes |= 1ULL << 42; /* 4TB */
|
|
break;
|
|
default:
|
|
page_sizes = 0;
|
|
}
|
|
|
|
cfg->pgsize_bitmap &= page_sizes;
|
|
cfg->ias = min(cfg->ias, max_addr_bits);
|
|
cfg->oas = min(cfg->oas, max_addr_bits);
|
|
}
|
|
|
|
static struct arm_lpae_io_pgtable *
|
|
arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
|
|
{
|
|
struct arm_lpae_io_pgtable *data;
|
|
int levels, va_bits, pg_shift;
|
|
|
|
arm_lpae_restrict_pgsizes(cfg);
|
|
|
|
if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K | SZ_64K)))
|
|
return NULL;
|
|
|
|
if (cfg->ias > ARM_LPAE_MAX_ADDR_BITS)
|
|
return NULL;
|
|
|
|
if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS)
|
|
return NULL;
|
|
|
|
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
pg_shift = __ffs(cfg->pgsize_bitmap);
|
|
data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
|
|
|
|
va_bits = cfg->ias - pg_shift;
|
|
levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
|
|
data->start_level = ARM_LPAE_MAX_LEVELS - levels;
|
|
|
|
/* Calculate the actual size of our pgd (without concatenation) */
|
|
data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
|
|
|
|
data->iop.ops = (struct io_pgtable_ops) {
|
|
.map = arm_lpae_map,
|
|
.map_pages = arm_lpae_map_pages,
|
|
.unmap = arm_lpae_unmap,
|
|
.unmap_pages = arm_lpae_unmap_pages,
|
|
.iova_to_phys = arm_lpae_iova_to_phys,
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
static struct io_pgtable *
|
|
arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
|
|
{
|
|
u64 reg;
|
|
struct arm_lpae_io_pgtable *data;
|
|
typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
|
|
bool tg1;
|
|
|
|
if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS |
|
|
IO_PGTABLE_QUIRK_NON_STRICT |
|
|
IO_PGTABLE_QUIRK_ARM_TTBR1))
|
|
return NULL;
|
|
|
|
data = arm_lpae_alloc_pgtable(cfg);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
/* TCR */
|
|
if (cfg->coherent_walk) {
|
|
tcr->sh = ARM_LPAE_TCR_SH_IS;
|
|
tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
|
|
tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
|
|
} else {
|
|
tcr->sh = ARM_LPAE_TCR_SH_OS;
|
|
tcr->irgn = ARM_LPAE_TCR_RGN_NC;
|
|
tcr->orgn = ARM_LPAE_TCR_RGN_NC;
|
|
}
|
|
|
|
tg1 = cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1;
|
|
switch (ARM_LPAE_GRANULE(data)) {
|
|
case SZ_4K:
|
|
tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_4K : ARM_LPAE_TCR_TG0_4K;
|
|
break;
|
|
case SZ_16K:
|
|
tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_16K : ARM_LPAE_TCR_TG0_16K;
|
|
break;
|
|
case SZ_64K:
|
|
tcr->tg = tg1 ? ARM_LPAE_TCR_TG1_64K : ARM_LPAE_TCR_TG0_64K;
|
|
break;
|
|
}
|
|
|
|
switch (cfg->oas) {
|
|
case 32:
|
|
tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
|
|
break;
|
|
case 36:
|
|
tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
|
|
break;
|
|
case 40:
|
|
tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
|
|
break;
|
|
case 42:
|
|
tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
|
|
break;
|
|
case 44:
|
|
tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
|
|
break;
|
|
case 48:
|
|
tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
|
|
break;
|
|
case 52:
|
|
tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
|
|
break;
|
|
default:
|
|
goto out_free_data;
|
|
}
|
|
|
|
tcr->tsz = 64ULL - cfg->ias;
|
|
|
|
/* MAIRs */
|
|
reg = (ARM_LPAE_MAIR_ATTR_NC
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
|
|
(ARM_LPAE_MAIR_ATTR_WBRWA
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
|
|
(ARM_LPAE_MAIR_ATTR_DEVICE
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
|
|
(ARM_LPAE_MAIR_ATTR_INC_OWBRWA
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE)) |
|
|
(ARM_LPAE_MAIR_ATTR_INC_OWBRANWA
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE_NWA)) |
|
|
(ARM_LPAE_MAIR_ATTR_IWBRWA_OWBRANWA
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_ICACHE_OCACHE_NWA));
|
|
|
|
cfg->arm_lpae_s1_cfg.mair = reg;
|
|
|
|
/* Looking good; allocate a pgd */
|
|
data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
|
|
GFP_KERNEL, cfg);
|
|
if (!data->pgd)
|
|
goto out_free_data;
|
|
|
|
/* Ensure the empty pgd is visible before any actual TTBR write */
|
|
wmb();
|
|
|
|
/* TTBR */
|
|
cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
|
|
return &data->iop;
|
|
|
|
out_free_data:
|
|
kfree(data);
|
|
return NULL;
|
|
}
|
|
|
|
static struct io_pgtable *
|
|
arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
|
|
{
|
|
u64 sl;
|
|
struct arm_lpae_io_pgtable *data;
|
|
typeof(&cfg->arm_lpae_s2_cfg.vtcr) vtcr = &cfg->arm_lpae_s2_cfg.vtcr;
|
|
|
|
/* The NS quirk doesn't apply at stage 2 */
|
|
if (cfg->quirks & ~(IO_PGTABLE_QUIRK_NON_STRICT))
|
|
return NULL;
|
|
|
|
data = arm_lpae_alloc_pgtable(cfg);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
/*
|
|
* Concatenate PGDs at level 1 if possible in order to reduce
|
|
* the depth of the stage-2 walk.
|
|
*/
|
|
if (data->start_level == 0) {
|
|
unsigned long pgd_pages;
|
|
|
|
pgd_pages = ARM_LPAE_PGD_SIZE(data) / sizeof(arm_lpae_iopte);
|
|
if (pgd_pages <= ARM_LPAE_S2_MAX_CONCAT_PAGES) {
|
|
data->pgd_bits += data->bits_per_level;
|
|
data->start_level++;
|
|
}
|
|
}
|
|
|
|
/* VTCR */
|
|
if (cfg->coherent_walk) {
|
|
vtcr->sh = ARM_LPAE_TCR_SH_IS;
|
|
vtcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
|
|
vtcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
|
|
} else {
|
|
vtcr->sh = ARM_LPAE_TCR_SH_OS;
|
|
vtcr->irgn = ARM_LPAE_TCR_RGN_NC;
|
|
vtcr->orgn = ARM_LPAE_TCR_RGN_NC;
|
|
}
|
|
|
|
sl = data->start_level;
|
|
|
|
switch (ARM_LPAE_GRANULE(data)) {
|
|
case SZ_4K:
|
|
vtcr->tg = ARM_LPAE_TCR_TG0_4K;
|
|
sl++; /* SL0 format is different for 4K granule size */
|
|
break;
|
|
case SZ_16K:
|
|
vtcr->tg = ARM_LPAE_TCR_TG0_16K;
|
|
break;
|
|
case SZ_64K:
|
|
vtcr->tg = ARM_LPAE_TCR_TG0_64K;
|
|
break;
|
|
}
|
|
|
|
switch (cfg->oas) {
|
|
case 32:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_32_BIT;
|
|
break;
|
|
case 36:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_36_BIT;
|
|
break;
|
|
case 40:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_40_BIT;
|
|
break;
|
|
case 42:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_42_BIT;
|
|
break;
|
|
case 44:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_44_BIT;
|
|
break;
|
|
case 48:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_48_BIT;
|
|
break;
|
|
case 52:
|
|
vtcr->ps = ARM_LPAE_TCR_PS_52_BIT;
|
|
break;
|
|
default:
|
|
goto out_free_data;
|
|
}
|
|
|
|
vtcr->tsz = 64ULL - cfg->ias;
|
|
vtcr->sl = ~sl & ARM_LPAE_VTCR_SL0_MASK;
|
|
|
|
/* Allocate pgd pages */
|
|
data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
|
|
GFP_KERNEL, cfg);
|
|
if (!data->pgd)
|
|
goto out_free_data;
|
|
|
|
/* Ensure the empty pgd is visible before any actual TTBR write */
|
|
wmb();
|
|
|
|
/* VTTBR */
|
|
cfg->arm_lpae_s2_cfg.vttbr = virt_to_phys(data->pgd);
|
|
return &data->iop;
|
|
|
|
out_free_data:
|
|
kfree(data);
|
|
return NULL;
|
|
}
|
|
|
|
static struct io_pgtable *
|
|
arm_32_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
|
|
{
|
|
if (cfg->ias > 32 || cfg->oas > 40)
|
|
return NULL;
|
|
|
|
cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
|
|
return arm_64_lpae_alloc_pgtable_s1(cfg, cookie);
|
|
}
|
|
|
|
static struct io_pgtable *
|
|
arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
|
|
{
|
|
if (cfg->ias > 40 || cfg->oas > 40)
|
|
return NULL;
|
|
|
|
cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
|
|
return arm_64_lpae_alloc_pgtable_s2(cfg, cookie);
|
|
}
|
|
|
|
static struct io_pgtable *
|
|
arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
|
|
{
|
|
struct arm_lpae_io_pgtable *data;
|
|
|
|
/* No quirks for Mali (hopefully) */
|
|
if (cfg->quirks)
|
|
return NULL;
|
|
|
|
if (cfg->ias > 48 || cfg->oas > 40)
|
|
return NULL;
|
|
|
|
cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G);
|
|
|
|
data = arm_lpae_alloc_pgtable(cfg);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
/* Mali seems to need a full 4-level table regardless of IAS */
|
|
if (data->start_level > 0) {
|
|
data->start_level = 0;
|
|
data->pgd_bits = 0;
|
|
}
|
|
/*
|
|
* MEMATTR: Mali has no actual notion of a non-cacheable type, so the
|
|
* best we can do is mimic the out-of-tree driver and hope that the
|
|
* "implementation-defined caching policy" is good enough. Similarly,
|
|
* we'll use it for the sake of a valid attribute for our 'device'
|
|
* index, although callers should never request that in practice.
|
|
*/
|
|
cfg->arm_mali_lpae_cfg.memattr =
|
|
(ARM_MALI_LPAE_MEMATTR_IMP_DEF
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
|
|
(ARM_MALI_LPAE_MEMATTR_WRITE_ALLOC
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
|
|
(ARM_MALI_LPAE_MEMATTR_IMP_DEF
|
|
<< ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV));
|
|
|
|
data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data), GFP_KERNEL,
|
|
cfg);
|
|
if (!data->pgd)
|
|
goto out_free_data;
|
|
|
|
/* Ensure the empty pgd is visible before TRANSTAB can be written */
|
|
wmb();
|
|
|
|
cfg->arm_mali_lpae_cfg.transtab = virt_to_phys(data->pgd) |
|
|
ARM_MALI_LPAE_TTBR_READ_INNER |
|
|
ARM_MALI_LPAE_TTBR_ADRMODE_TABLE;
|
|
if (cfg->coherent_walk)
|
|
cfg->arm_mali_lpae_cfg.transtab |= ARM_MALI_LPAE_TTBR_SHARE_OUTER;
|
|
|
|
return &data->iop;
|
|
|
|
out_free_data:
|
|
kfree(data);
|
|
return NULL;
|
|
}
|
|
|
|
struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
|
|
.alloc = arm_64_lpae_alloc_pgtable_s1,
|
|
.free = arm_lpae_free_pgtable,
|
|
};
|
|
|
|
struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = {
|
|
.alloc = arm_64_lpae_alloc_pgtable_s2,
|
|
.free = arm_lpae_free_pgtable,
|
|
};
|
|
|
|
struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns = {
|
|
.alloc = arm_32_lpae_alloc_pgtable_s1,
|
|
.free = arm_lpae_free_pgtable,
|
|
};
|
|
|
|
struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = {
|
|
.alloc = arm_32_lpae_alloc_pgtable_s2,
|
|
.free = arm_lpae_free_pgtable,
|
|
};
|
|
|
|
struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
|
|
.alloc = arm_mali_lpae_alloc_pgtable,
|
|
.free = arm_lpae_free_pgtable,
|
|
};
|
|
|
|
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
|
|
|
|
static struct io_pgtable_cfg *cfg_cookie __initdata;
|
|
|
|
static void __init dummy_tlb_flush_all(void *cookie)
|
|
{
|
|
WARN_ON(cookie != cfg_cookie);
|
|
}
|
|
|
|
static void __init dummy_tlb_flush(unsigned long iova, size_t size,
|
|
size_t granule, void *cookie)
|
|
{
|
|
WARN_ON(cookie != cfg_cookie);
|
|
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
|
|
}
|
|
|
|
static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
|
|
unsigned long iova, size_t granule,
|
|
void *cookie)
|
|
{
|
|
dummy_tlb_flush(iova, granule, granule, cookie);
|
|
}
|
|
|
|
static const struct iommu_flush_ops dummy_tlb_ops __initconst = {
|
|
.tlb_flush_all = dummy_tlb_flush_all,
|
|
.tlb_flush_walk = dummy_tlb_flush,
|
|
.tlb_add_page = dummy_tlb_add_page,
|
|
};
|
|
|
|
static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops)
|
|
{
|
|
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
|
|
pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n",
|
|
cfg->pgsize_bitmap, cfg->ias);
|
|
pr_err("data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_per_level, pgd @ %p\n",
|
|
ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data),
|
|
ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd);
|
|
}
|
|
|
|
#define __FAIL(ops, i) ({ \
|
|
WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \
|
|
arm_lpae_dump_ops(ops); \
|
|
selftest_running = false; \
|
|
-EFAULT; \
|
|
})
|
|
|
|
static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|
{
|
|
static const enum io_pgtable_fmt fmts[] __initconst = {
|
|
ARM_64_LPAE_S1,
|
|
ARM_64_LPAE_S2,
|
|
};
|
|
|
|
int i, j;
|
|
unsigned long iova;
|
|
size_t size;
|
|
struct io_pgtable_ops *ops;
|
|
|
|
selftest_running = true;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fmts); ++i) {
|
|
cfg_cookie = cfg;
|
|
ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg);
|
|
if (!ops) {
|
|
pr_err("selftest: failed to allocate io pgtable ops\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*
|
|
* Initial sanity checks.
|
|
* Empty page tables shouldn't provide any translations.
|
|
*/
|
|
if (ops->iova_to_phys(ops, 42))
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, SZ_1G + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, SZ_2G + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
/*
|
|
* Distinct mappings of different granule sizes.
|
|
*/
|
|
iova = 0;
|
|
for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
|
|
size = 1UL << j;
|
|
|
|
if (ops->map(ops, iova, iova, size, IOMMU_READ |
|
|
IOMMU_WRITE |
|
|
IOMMU_NOEXEC |
|
|
IOMMU_CACHE, GFP_KERNEL))
|
|
return __FAIL(ops, i);
|
|
|
|
/* Overlapping mappings */
|
|
if (!ops->map(ops, iova, iova + size, size,
|
|
IOMMU_READ | IOMMU_NOEXEC, GFP_KERNEL))
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
iova += SZ_1G;
|
|
}
|
|
|
|
/* Partial unmap */
|
|
size = 1UL << __ffs(cfg->pgsize_bitmap);
|
|
if (ops->unmap(ops, SZ_1G + size, size, NULL) != size)
|
|
return __FAIL(ops, i);
|
|
|
|
/* Remap of partial unmap */
|
|
if (ops->map(ops, SZ_1G + size, size, size, IOMMU_READ, GFP_KERNEL))
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, SZ_1G + size + 42) != (size + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
/* Full unmap */
|
|
iova = 0;
|
|
for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
|
|
size = 1UL << j;
|
|
|
|
if (ops->unmap(ops, iova, size, NULL) != size)
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, iova + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
/* Remap full block */
|
|
if (ops->map(ops, iova, iova, size, IOMMU_WRITE, GFP_KERNEL))
|
|
return __FAIL(ops, i);
|
|
|
|
if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
|
|
return __FAIL(ops, i);
|
|
|
|
iova += SZ_1G;
|
|
}
|
|
|
|
free_io_pgtable_ops(ops);
|
|
}
|
|
|
|
selftest_running = false;
|
|
return 0;
|
|
}
|
|
|
|
static int __init arm_lpae_do_selftests(void)
|
|
{
|
|
static const unsigned long pgsize[] __initconst = {
|
|
SZ_4K | SZ_2M | SZ_1G,
|
|
SZ_16K | SZ_32M,
|
|
SZ_64K | SZ_512M,
|
|
};
|
|
|
|
static const unsigned int ias[] __initconst = {
|
|
32, 36, 40, 42, 44, 48,
|
|
};
|
|
|
|
int i, j, pass = 0, fail = 0;
|
|
struct io_pgtable_cfg cfg = {
|
|
.tlb = &dummy_tlb_ops,
|
|
.oas = 48,
|
|
.coherent_walk = true,
|
|
};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pgsize); ++i) {
|
|
for (j = 0; j < ARRAY_SIZE(ias); ++j) {
|
|
cfg.pgsize_bitmap = pgsize[i];
|
|
cfg.ias = ias[j];
|
|
pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u\n",
|
|
pgsize[i], ias[j]);
|
|
if (arm_lpae_run_tests(&cfg))
|
|
fail++;
|
|
else
|
|
pass++;
|
|
}
|
|
}
|
|
|
|
pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail);
|
|
return fail ? -EFAULT : 0;
|
|
}
|
|
subsys_initcall(arm_lpae_do_selftests);
|
|
#endif
|