https://source.android.com/docs/security/bulletin/2022-10-01 CVE-2022-1786 CVE-2022-20421 CVE-2022-20422 CVE-2022-20423 CVE-2022-20409 * tag 'ASB-2022-10-01_4.19-stable' of https://android.googlesource.com/kernel/common: Linux 4.19.261 clk: iproc: Do not rely on node name for correct PLL setup selftests: Fix the if conditions of in test_extra_filter() nvme: Fix IOC_PR_CLEAR and IOC_PR_RELEASE ioctls for nvme devices nvme: add new line after variable declatation usbnet: Fix memory leak in usbnet_disconnect() Input: melfas_mip4 - fix return value check in mip4_probe() Revert "drm: bridge: analogix/dp: add panel prepare/unprepare in suspend/resume time" soc: sunxi: sram: Fix debugfs info for A64 SRAM C soc: sunxi: sram: Fix probe function ordering issues soc: sunxi: sram: Prevent the driver from being unbound soc: sunxi: sram: Actually claim SRAM regions ima: Free the entire rule if it fails to parse ima: Free the entire rule when deleting a list of rules ima: Have the LSM free its audit rule mm/migrate_device.c: flush TLB while holding PTL mm: prevent page_frag_alloc() from corrupting the memory mm/page_alloc: fix race condition between build_all_zonelists and page allocation mmc: moxart: fix 4-bit bus width and remove 8-bit bus width libata: add ATA_HORKAGE_NOLPM for Pioneer BDR-207M and BDR-205 ntfs: fix BUG_ON in ntfs_lookup_inode_by_name() ARM: dts: integrator: Tag PCI host with device_type net: usb: qmi_wwan: Add new usb-id for Dell branded EM7455 uas: ignore UAS for Thinkplus chips usb-storage: Add Hiksemi USB3-FW to IGNORE_UAS uas: add no-uas quirk for Hiksemi usb_disk Linux 4.19.260 ext4: make directory inode spreading reflect flexbg size usb: dwc3: pci: Allow Elkhart Lake to utilize DSM method for PM functionality workqueue: don't skip lockdep work dependency in cancel_work_sync() drm/rockchip: Fix return type of cdn_dp_connector_mode_valid drm/amd/display: Limit user regamma to a valid value Drivers: hv: Never allocate anything besides framebuffer from framebuffer memory region s390/dasd: fix Oops in dasd_alias_get_start_dev due to missing pavgroup serial: tegra: Use uart_xmit_advance(), fixes icount.tx accounting serial: Create uart_xmit_advance() net: sunhme: Fix packet reception for len < RX_COPY_THRESHOLD perf kcore_copy: Do not check /proc/modules is unchanged perf jit: Include program header in ELF files can: gs_usb: gs_can_open(): fix race dev->can.state condition netfilter: ebtables: fix memory leak when blob is malformed of: mdio: Add of_node_put() when breaking out of for_each_xx i40e: Fix set max_tx_rate when it is lower than 1 Mbps i40e: Fix VF set max MTU size MIPS: lantiq: export clk_get_io() for lantiq_wdt.ko net: team: Unsync device addresses on ndo_stop ipvlan: Fix out-of-bound bugs caused by unset skb->mac_header iavf: Fix cached head and tail value for iavf_get_tx_pending netfilter: nf_conntrack_irc: Tighten matching on DCC message netfilter: nf_conntrack_sip: fix ct_sip_walk_headers arm64: dts: rockchip: Remove 'enable-active-low' from rk3399-puma arm64: dts: rockchip: Set RK3399-Gru PCLK_EDP to 24 MHz mm/slub: fix to return errno if kmalloc() fails efi: libstub: check Shim mode using MokSBStateRT ALSA: hda/realtek: Enable 4-speaker output Dell Precision 5530 laptop ALSA: hda: add Intel 5 Series / 3400 PCI DID ALSA: hda/tegra: set depop delay for tegra USB: serial: option: add Quectel RM520N USB: serial: option: add Quectel BG95 0x0203 composition USB: core: Fix RST error in hub.c wifi: mac80211: Fix UAF in ieee80211_scan_rx() usb: dwc3: pci: add support for the Intel Alder Lake-S usb: dwc3: pci: add support for the Intel Jasper Lake usb: dwc3: pci: add support for the Intel Tiger Lake PCH -H variant usb: dwc3: pci: add support for TigerLake Devices usb: dwc3: pci: Add Support for Intel Elkhart Lake Devices ALSA: hda/sigmatel: Fix unused variable warning for beep power change video: fbdev: pxa3xx-gcu: Fix integer overflow in pxa3xx_gcu_write mksysmap: Fix the mismatch of 'L0' symbols in System.map MIPS: OCTEON: irq: Fix octeon_irq_force_ciu_mapping() net: usb: qmi_wwan: add Quectel RM520N ALSA: hda/sigmatel: Keep power up while beep is enabled rxrpc: Fix local destruction being repeated regulator: pfuze100: Fix the global-out-of-bounds access in pfuze100_regulator_probe() ASoC: nau8824: Fix semaphore unbalance at error paths cifs: don't send down the destination address to sendmsg for a SOCK_STREAM mvpp2: no need to check return value of debugfs_create functions nvmet: fix a use-after-free parisc: ccio-dma: Add missing iounmap in error path in ccio_probe() drm/meson: Correct OSD1 global alpha value gpio: mpc8xxx: Fix support for IRQ_TYPE_LEVEL_LOW flow_type in mpc85xx of: fdt: fix off-by-one error in unflatten_dt_nodes() Revert "xhci: Add grace period after xHC start to prevent premature runtime suspend." Revert "USB: core: Prevent nested device-reset calls" Revert "mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse" Revert "sched/deadline: Fix priority inheritance with multiple scheduling classes" Revert "kernel/sched: Remove dl_boosted flag comment" Revert "fs: check FMODE_LSEEK to control internal pipe splicing" Linux 4.19.259 tracefs: Only clobber mode/uid/gid on remount if asked net: dp83822: disable rx error interrupt mm: Fix TLB flush for not-first PFNMAP mappings in unmap_region() usb: storage: Add ASUS <0x0b05:0x1932> to IGNORE_UAS platform/x86: acer-wmi: Acer Aspire One AOD270/Packard Bell Dot keymap fixes perf/arm_pmu_platform: fix tests for platform_get_irq() failure Input: iforce - add support for Boeder Force Feedback Wheel ieee802154: cc2520: add rc code in cc2520_tx() tg3: Disable tg3 device on system reboot to avoid triggering AER HID: ishtp-hid-clientHID: ishtp-hid-client: Fix comment typo drm/msm/rd: Fix FIFO-full deadlock Linux 4.19.258 SUNRPC: use _bh spinlocking on ->transport_lock MIPS: loongson32: ls1c: Fix hang during startup x86/nospec: Fix i386 RSB stuffing usb: dwc3: qcom: fix use-after-free on runtime-PM wakeup USB: serial: ch341: fix disabled rx timer on older devices USB: serial: ch341: fix lost character on LCR updates usb: dwc3: fix PHY disable sequence sch_sfb: Also store skb len before calling child enqueue tcp: fix early ETIMEDOUT after spurious non-SACK RTO RDMA/mlx5: Set local port to one when accessing counters ipv6: sr: fix out-of-bounds read when setting HMAC data. i40e: Fix kernel crash during module removal tipc: fix shift wrapping bug in map_get() sch_sfb: Don't assume the skb is still around after enqueueing to child netfilter: nf_conntrack_irc: Fix forged IP logic netfilter: br_netfilter: Drop dst references before setting. soc: brcmstb: pm-arm: Fix refcount leak and __iomem leak bugs scsi: mpt3sas: Fix use-after-free warning debugfs: add debugfs_lookup_and_remove() kprobes: Prohibit probes in gate area ALSA: usb-audio: Fix an out-of-bounds bug in __snd_usb_parse_audio_interface() ALSA: aloop: Fix random zeros in capture data when using jiffies timer ALSA: emu10k1: Fix out of bounds access in snd_emu10k1_pcm_channel_alloc() drm/amdgpu: mmVM_L2_CNTL3 register not initialized correctly fbdev: chipsfb: Add missing pci_disable_device() in chipsfb_pci_init() arm64: cacheinfo: Fix incorrect assignment of signed error value to unsigned fw_level parisc: Add runtime check to prevent PA2.0 kernels on PA1.x machines parisc: ccio-dma: Handle kmalloc failure in ccio_init_resources() drm/radeon: add a force flush to delay work when radeon drm/amdgpu: Check num_gfx_rings for gfx v9_0 rb setup. ALSA: seq: Fix data-race at module auto-loading ALSA: seq: oss: Fix data-race for max_midi_devs access net: mac802154: Fix a condition in the receive path wifi: mac80211: Don't finalize CSA in IBSS mode if state is disconnected usb: gadget: mass_storage: Fix cdrom data transfers on MAC-OS USB: core: Prevent nested device-reset calls s390: fix nospec table alignments s390/hugetlb: fix prepare_hugepage_range() check for 2 GB hugepages usb-storage: Add ignore-residue quirk for NXP PN7462AU USB: cdc-acm: Add Icom PMR F3400 support (0c26:0020) usb: dwc2: fix wrong order of phy_power_on and phy_init usb: typec: altmodes/displayport: correct pin assignment for UFP receptacles USB: serial: option: add support for Cinterion MV32-WA/WB RmNet mode USB: serial: option: add Quectel EM060K modem USB: serial: option: add support for OPPO R11 diag port USB: serial: cp210x: add Decagon UCA device id xhci: Add grace period after xHC start to prevent premature runtime suspend. thunderbolt: Use the actual buffer in tb_async_error() hwmon: (gpio-fan) Fix array out of bounds access Input: rk805-pwrkey - fix module autoloading clk: core: Fix runtime PM sequence in clk_core_unprepare() Revert "clk: core: Honor CLK_OPS_PARENT_ENABLE for clk gate ops" clk: core: Honor CLK_OPS_PARENT_ENABLE for clk gate ops drm/i915/reg: Fix spelling mistake "Unsupport" -> "Unsupported" binder: fix UAF of ref->proc caused by race condition USB: serial: ftdi_sio: add Omron CS1W-CIF31 device id vt: Clear selection before changing the font staging: rtl8712: fix use after free bugs serial: fsl_lpuart: RS485 RTS polariy is inverse net/smc: Remove redundant refcount increase Revert "sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued skb" tcp: annotate data-race around challenge_timestamp sch_cake: Return __NET_XMIT_STOLEN when consuming enqueued skb kcm: fix strp_init() order and cleanup ethernet: rocker: fix sleep in atomic context bug in neigh_timer_handler Revert "xhci: turn off port power in shutdown" wifi: cfg80211: debugfs: fix return type in ht40allow_map_read() ieee802154/adf7242: defer destroy_workqueue call platform/x86: pmc_atom: Fix SLP_TYPx bitfield mask drm/msm/dsi: Fix number of regulators for msm8996_dsi_cfg drm/msm/dsi: fix the inconsistent indenting net: dp83822: disable false carrier interrupt Revert "mm: kmemleak: take a full lowmem check in kmemleak_*_phys()" fs: only do a memory barrier for the first set_buffer_uptodate() wifi: iwlegacy: 4965: corrected fix for potential off-by-one overflow in il4965_rs_fill_link_cmd() efi: capsule-loader: Fix use-after-free in efi_capsule_write driver core: Don't probe devices after bus_type.match() probe deferral Revert "USB: HCD: Fix URB giveback issue in tasklet function" Linux 4.19.257 net: neigh: don't call kfree_skb() under spin_lock_irqsave() kprobes: don't call disarm_kprobe() for disabled kprobes netfilter: conntrack: NF_CONNTRACK_PROCFS should no longer default to y s390/hypfs: avoid error message under KVM neigh: fix possible DoS due to net iface start/stop loop drm/amd/display: clear optc underflow before turn off odm clock mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse ftrace: Fix NULL pointer dereference in is_ftrace_trampoline when ftrace is dead fbdev: fb_pm2fb: Avoid potential divide by zero error HID: hidraw: fix memory leak in hidraw_release() media: pvrusb2: fix memory leak in pvr_probe HID: steam: Prevent NULL pointer dereference in steam_{recv,send}_report Bluetooth: L2CAP: Fix build errors in some archs kbuild: Fix include path in scripts/Makefile.modpost x86/bugs: Add "unknown" reporting for MMIO Stale Data s390/mm: do not trigger write fault when vma does not allow VM_WRITE selftests/bpf: Fix test_align verifier log patterns bpf: Fix the off-by-two error in range markings arm64: map FDT as RW for early_init_dt_scan() mm: Force TLB flush for PFNMAP mappings before unlink_file_vma() scsi: storvsc: Remove WQ_MEM_RECLAIM from storvsc_error_wq md: call __md_stop_writes in md_stop mm/hugetlb: fix hugetlb not supporting softdirty tracking s390: fix double free of GS and RI CBs on fork() failure asm-generic: sections: refactor memory_intersects loop: Check for overflow while configuring loop x86/unwind/orc: Unwind ftrace trampolines with correct ORC entry btrfs: check if root is readonly while setting security xattr ixgbe: stop resetting SYSTIME in ixgbe_ptp_start_cyclecounter net: Fix a data-race around sysctl_somaxconn. net: Fix a data-race around netdev_budget_usecs. net: Fix a data-race around netdev_budget. net: Fix a data-race around sysctl_net_busy_read. net: Fix a data-race around sysctl_net_busy_poll. net: Fix a data-race around sysctl_tstamp_allow_data. ratelimit: Fix data-races in ___ratelimit(). net: Fix data-races around netdev_tstamp_prequeue. net: Fix data-races around weight_p and dev_weight_[rt]x_bias. netfilter: nft_tunnel: restrict it to netdev family netfilter: nft_osf: restrict osf to ipv4, ipv6 and inet families netfilter: nft_payload: do not truncate csum_offset and csum_type netfilter: nft_payload: report ERANGE for too long offset and length netfilter: ebtables: reject blobs that don't provide all entry points net: ipvtap - add __init/__exit annotations to module init/exit funcs bonding: 802.3ad: fix no transmission of LACPDUs rose: check NULL rose_loopback_neigh->loopback af_key: Do not call xfrm_probe_algs in parallel xfrm: fix refcount leak in __xfrm_policy_check() kernel/sched: Remove dl_boosted flag comment sched/deadline: Fix priority inheritance with multiple scheduling classes sched/deadline: Fix stale throttling on de-/boosted tasks sched/deadline: Unthrottle PI boosted threads while enqueuing pinctrl: amd: Don't save/restore interrupt status and wake status bits kernel/sys_ni: add compat entry for fadvise64_64 parisc: Fix exception handler for fldw and fstw instructions audit: fix potential double free on error path from fsnotify_add_inode_mark Linux 4.19.256 btrfs: raid56: don't trust any cached sector in __raid56_parity_recover() btrfs: only write the sectors in the vertical stripe which has data stripes tracing/probes: Have kprobes and uprobes use $COMM too tee: add overflow check in register_shm_helper() MIPS: tlbex: Explicitly compare _PAGE_NO_EXEC against 0 video: fbdev: i740fb: Check the argument of i740_calc_vclk() powerpc/64: Init jump labels before parse_early_param() smb3: check xattr value length earlier f2fs: fix to avoid use f2fs_bug_on() in f2fs_new_node_page() ALSA: timer: Use deferred fasync helper ALSA: core: Add async signal helpers watchdog: export lockup_detector_reconfigure RISC-V: Add fast call path of crash_kexec() riscv: mmap with PROT_WRITE but no PROT_READ is invalid mips: cavium-octeon: Fix missing of_node_put() in octeon2_usb_clocks_start vfio: Clear the caps->buf to NULL after free tty: serial: Fix refcount leak bug in ucc_uart.c lib/list_debug.c: Detect uninitialized lists ext4: avoid resizing to a partial cluster size ext4: avoid remove directory when directory is corrupted drivers:md:fix a potential use-after-free bug dmaengine: sprd: Cleanup in .remove() after pm_runtime_get_sync() failed cxl: Fix a memory leak in an error handling path gadgetfs: ep_io - wait until IRQ finishes clk: qcom: ipq8074: dont disable gcc_sleep_clk_src vboxguest: Do not use devm for irq usb: renesas: Fix refcount leak bug usb: host: ohci-ppc-of: Fix refcount leak bug irqchip/tegra: Fix overflow implicit truncation warnings PCI: Add ACS quirk for Broadcom BCM5750x NICs drm/meson: Fix refcount bugs in meson_vpu_has_available_connectors() locking/atomic: Make test_and_*_bit() ordered on failure gcc-plugins: Undefine LATENT_ENTROPY_PLUGIN when plugin disabled for a file igb: Add lock to avoid data race fec: Fix timer capture timing in `fec_ptp_enable_pps()` i40e: Fix to stop tx_timeout recovery if GLOBR fails powerpc/pci: Fix get_phb_number() locking netfilter: nf_tables: really skip inactive sets when allocating name nios2: add force_successful_syscall_return() nios2: restarts apply only to the first sigframe we build... nios2: fix syscall restart checks nios2: traced syscall does need to check the syscall number nios2: don't leave NULLs in sys_call_table[] nios2: page fault et.al. are *not* restartable syscalls... atm: idt77252: fix use-after-free bugs caused by tst_timer xen/xenbus: fix return type in xenbus_file_read() NTB: ntb_tool: uninitialized heap data in tool_fn_write() tools build: Switch to new openssl API for test-libcrypto vsock: Set socket state back to SS_UNCONNECTED in vsock_connect_timeout() vsock: Fix memory leak in vsock_connect() geneve: do not use RT_TOS for IPv6 flowlabel ACPI: property: Return type of acpi_add_nondev_subnodes() should be bool pinctrl: qcom: msm8916: Allow CAMSS GP clocks to be muxed pinctrl: nomadik: Fix refcount leak in nmk_pinctrl_dt_subnode_to_map SUNRPC: Reinitialise the backchannel request buffers before reuse NFSv4/pnfs: Fix a use-after-free bug in open NFSv4.1: RECLAIM_COMPLETE must handle EACCES NFSv4: Fix races in the legacy idmapper upcall apparmor: Fix memleak in aa_simple_write_to_buffer() apparmor: fix reference count leak in aa_pivotroot() apparmor: fix overlapping attachment computation apparmor: fix aa_label_asxprint return check apparmor: Fix failed mount permission check error message apparmor: fix absroot causing audited secids to begin with = apparmor: fix quiet_denied for file rules can: ems_usb: fix clang's -Wunaligned-access warning tracing: Have filter accept "common_cpu" to be consistent btrfs: fix lost error handling when looking up extended ref on log replay mmc: pxamci: Fix an error handling path in pxamci_probe() mmc: pxamci: Fix another error handling path in pxamci_probe() ata: libata-eh: Add missing command name rds: add missing barrier to release_refill ALSA: info: Fix llseek return value when using callback powerpc/ptdump: Fix display of RW pages on FSL_BOOK3E powerpc/mm: Split dump_pagelinuxtables flag_array table firmware: arm_scpi: Ensure scpi_info is not assigned if the probe fails net_sched: cls_route: disallow handle of 0 net/9p: Initialize the iounit field during fid creation Bluetooth: L2CAP: Fix l2cap_global_chan_by_psm regression Revert "net: usb: ax88179_178a needs FLAG_SEND_ZLP" scsi: sg: Allow waiting for commands to complete on removed device tcp: fix over estimation in sk_forced_mem_schedule() KVM: x86: Avoid theoretical NULL pointer dereference in kvm_irq_delivery_to_apic_fast() KVM: x86: Check lapic_in_kernel() before attempting to set a SynIC irq KVM: Add infrastructure and macro to mark VM as bugged btrfs: reject log replay if there is unsupported RO compat flag net_sched: cls_route: remove from list when handle is 0 ACPI: CPPC: Do not prevent CPPC from working in the future dm writecache: set a default MAX_WRITEBACK_JOBS dm raid: fix address sanitizer warning in raid_status dm raid: fix address sanitizer warning in raid_resume intel_th: pci: Add Meteor Lake-P support intel_th: pci: Add Raptor Lake-S PCH support intel_th: pci: Add Raptor Lake-S CPU support ext4: correct the misjudgment in ext4_iget_extra_inode ext4: correct max_inline_xattr_value_size computing ext4: fix extent status tree race in writeback error recovery path ext4: update s_overhead_clusters in the superblock during an on-line resize ext4: fix use-after-free in ext4_xattr_set_entry ext4: make sure ext4_append() always allocates new block ext4: add EXT4_INODE_HAS_XATTR_SPACE macro in xattr.h spmi: trace: fix stack-out-of-bound access in SPMI tracing functions x86/olpc: fix 'logical not is only applied to the left hand side' scsi: zfcp: Fix missing auto port scan and thus missing target ports video: fbdev: s3fb: Check the size of screen before memset_io() video: fbdev: arkfb: Check the size of screen before memset_io() video: fbdev: vt8623fb: Check the size of screen before memset_io() tools/thermal: Fix possible path truncations video: fbdev: arkfb: Fix a divide-by-zero bug in ark_set_pixclock() x86/numa: Use cpumask_available instead of hardcoded NULL check scripts/faddr2line: Fix vmlinux detection on arm64 genelf: Use HAVE_LIBCRYPTO_SUPPORT, not the never defined HAVE_LIBCRYPTO powerpc/pci: Fix PHB numbering when using opal-phbid kprobes: Forbid probing on trampoline and BPF code areas powerpc/cell/axon_msi: Fix refcount leak in setup_msi_msg_address powerpc/xive: Fix refcount leak in xive_get_max_prio powerpc/spufs: Fix refcount leak in spufs_init_isolated_loader powerpc/pci: Prefer PCI domain assignment via DT 'linux,pci-domain' and alias powerpc/32: Do not allow selection of e5500 or e6500 CPUs on PPC32 video: fbdev: sis: fix typos in SiS_GetModeID() video: fbdev: amba-clcd: Fix refcount leak bugs ASoC: qcom: q6dsp: Fix an off-by-one in q6adm_alloc_copp() s390/zcore: fix race when reading from hardware system area iommu/arm-smmu: qcom_iommu: Add of_node_put() when breaking out of loop mfd: t7l66xb: Drop platform disable callback kfifo: fix kfifo_to_user() return type rpmsg: qcom_smd: Fix refcount leak in qcom_smd_parse_edge iommu/exynos: Handle failed IOMMU device registration properly tty: n_gsm: fix missing corner cases in gsmld_poll() tty: n_gsm: fix DM command tty: n_gsm: fix wrong T1 retry count handling vfio/ccw: Do not change FSM state in subchannel event remoteproc: qcom: wcnss: Fix handling of IRQs tty: n_gsm: fix race condition in gsmld_write() tty: n_gsm: fix packet re-transmission without open control channel tty: n_gsm: fix non flow control frames during mux flow off profiling: fix shift too large makes kernel panic serial: 8250_dw: Store LSR into lsr_saved_flags in dw8250_tx_wait_empty() ASoC: mediatek: mt8173-rt5650: Fix refcount leak in mt8173_rt5650_dev_probe ASoC: codecs: da7210: add check for i2c_add_driver ASoC: mt6797-mt6351: Fix refcount leak in mt6797_mt6351_dev_probe ASoC: mediatek: mt8173: Fix refcount leak in mt8173_rt5650_rt5676_dev_probe jbd2: fix assertion 'jh->b_frozen_data == NULL' failure when journal aborted ext4: recover csum seed of tmp_inode after migrating to extents null_blk: fix ida error handling in null_add_dev() RDMA/rxe: Fix error unwind in rxe_create_qp() mm/mmap.c: fix missing call to vm_unacct_memory in mmap_region platform/olpc: Fix uninitialized data in debugfs write USB: serial: fix tty-port initialized comments HID: alps: Declare U1_UNICORN_LEGACY support mmc: cavium-thunderx: Add of_node_put() when breaking out of loop mmc: cavium-octeon: Add of_node_put() when breaking out of loop gpio: gpiolib-of: Fix refcount bugs in of_mm_gpiochip_add_data() RDMA/hfi1: fix potential memory leak in setup_base_ctxt() usb: gadget: udc: amd5536 depends on HAS_DMA scsi: smartpqi: Fix DMA direction for RAID requests mmc: sdhci-of-at91: fix set_uhs_signaling rewriting of MC1R memstick/ms_block: Fix a memory leak memstick/ms_block: Fix some incorrect memory allocation mmc: sdhci-of-esdhc: Fix refcount leak in esdhc_signal_voltage_switch staging: rtl8192u: Fix sleep in atomic context bug in dm_fsync_timer_callback soundwire: bus_type: fix remove and shutdown support clk: qcom: ipq8074: set BRANCH_HALT_DELAY flag for UBI clocks clk: qcom: ipq8074: fix NSS port frequency tables misc: rtsx: Fix an error handling path in rtsx_pci_probe() usb: ohci-nxp: Fix refcount leak in ohci_hcd_nxp_probe usb: host: Fix refcount leak in ehci_hcd_ppc_of_probe fpga: altera-pr-ip: fix unsigned comparison with less than zero mtd: st_spi_fsm: Add a clk_disable_unprepare() in .probe()'s error path mtd: sm_ftl: Fix deadlock caused by cancel_work_sync in sm_release HID: cp2112: prevent a buffer overflow in cp2112_xfer() mtd: maps: Fix refcount leak in ap_flash_init mtd: maps: Fix refcount leak in of_flash_probe_versatile clk: renesas: r9a06g032: Fix UART clkgrp bitsel dccp: put dccp_qpolicy_full() and dccp_qpolicy_push() in the same lock net: rose: fix netdev reference changes netdevsim: Avoid allocation warnings triggered from user space net/mlx5e: Fix the value of MLX5E_MAX_RQ_NUM_MTTS wifi: libertas: Fix possible refcount leak in if_usb_probe() wifi: wil6210: debugfs: fix uninitialized variable use in `wil_write_file_wmi()` i2c: mux-gpmux: Add of_node_put() when breaking out of loop i2c: cadence: Support PEC for SMBus block read Bluetooth: hci_intel: Add check for platform_driver_register can: pch_can: pch_can_error(): initialize errc before using it can: error: specify the values of data[5..7] of CAN error frames can: usb_8dev: do not report txerr and rxerr during bus-off can: kvaser_usb_leaf: do not report txerr and rxerr during bus-off can: kvaser_usb_hydra: do not report txerr and rxerr during bus-off can: sun4i_can: do not report txerr and rxerr during bus-off can: hi311x: do not report txerr and rxerr during bus-off can: sja1000: do not report txerr and rxerr during bus-off can: rcar_can: do not report txerr and rxerr during bus-off can: pch_can: do not report txerr and rxerr during bus-off wifi: p54: add missing parentheses in p54_flush() wifi: p54: Fix an error handling path in p54spi_probe() selftests: timers: clocksource-switch: fix passing errors from child wifi: wil6210: debugfs: fix info leak in wil_write_file_wmi() selftests: timers: valid-adjtimex: build fix for newer toolchains fs: check FMODE_LSEEK to control internal pipe splicing libbpf: Fix the name of a reused map tcp: make retransmitted SKB fit into the send window mediatek: mt76: mac80211: Fix missing of_node_put() in mt76_led_init() media: platform: mtk-mdp: Fix mdp_ipi_comm structure alignment crypto: hisilicon - Kunpeng916 crypto driver don't sleep when in softirq drm/msm/mdp5: Fix global state lock backoff drm: bridge: sii8620: fix possible off-by-one drm/mediatek: dpi: Remove output format of YUV drm/rockchip: vop: Don't crash for invalid duplicate_state() drm/vc4: dsi: Correct DSI divider calculations media: hdpvr: fix error value returns in hdpvr_read drm: bridge: adv7511: Add check for mipi_dsi_driver_register wifi: iwlegacy: 4965: fix potential off-by-one overflow in il4965_rs_fill_link_cmd() ath9k: fix use-after-free in ath9k_hif_usb_rx_cb media: tw686x: Register the irq at the end of probe i2c: Fix a potential use after free drm/mediatek: Add pull-down MIPI operation in mtk_dsi_poweroff function drm/radeon: fix potential buffer overflow in ni_set_mc_special_registers() wifi: rtlwifi: fix error codes in rtl_debugfs_set_write_h2c() ath10k: do not enforce interrupt trigger type dm: return early from dm_pr_call() if DM device is suspended thermal/tools/tmon: Include pthread and time headers in tmon.h nohz/full, sched/rt: Fix missed tick-reenabling bug in dequeue_task_rt() regulator: of: Fix refcount leak bug in of_get_regulation_constraints() arm64: dts: qcom: msm8916: Fix typo in pronto remoteproc node bus: hisi_lpc: fix missing platform_device_put() in hisi_lpc_acpi_probe() ARM: dts: qcom: pm8841: add required thermal-sensor-cells cpufreq: zynq: Fix refcount leak in zynq_get_revision ARM: OMAP2+: Fix refcount leak in omap3xxx_prm_late_init soc: fsl: guts: machine variable might be unset ARM: dts: ast2500-evb: fix board compatible x86/pmem: Fix platform-device leak in error path ARM: bcm: Fix refcount leak in bcm_kona_smc_init meson-mx-socinfo: Fix refcount leak in meson_mx_socinfo_init ARM: findbit: fix overflowing offset selinux: Add boundary check in put_entry() PM: hibernate: defer device probing when resuming from hibernation arm64: dts: qcom: ipq8074: fix NAND node name ACPI: LPSS: Fix missing check in register_device_clock() ACPI: PM: save NVS memory for Lenovo G40-45 ACPI: EC: Remove duplicate ThinkPad X1 Carbon 6th entry from DMI quirks ARM: OMAP2+: display: Fix refcount leak bug ARM: dts: imx6ul: fix qspi node compatible ARM: dts: imx6ul: fix lcdif node compatible ARM: dts: imx6ul: change operating-points to uint32-matrix ARM: dts: imx6ul: add missing properties for sram ext2: Add more validity checks for inode counts USB: HCD: Fix URB giveback issue in tasklet function arm64: fix oops in concurrently setting insn_emulation sysctls arm64: Do not forget syscall when starting a new thread. netfilter: nf_tables: fix null deref due to zeroed list head netfilter: nf_tables: do not allow SET_ID to refer to another table MIPS: cpuinfo: Fix a warning for CONFIG_CPUMASK_OFFSTACK powerpc/powernv: Avoid crashing if rng is NULL powerpc/fsl-pci: Fix Class Code of PCIe Root Port PCI: Add defines for normal and subtractive PCI bridges ia64, processor: fix -Wincompatible-pointer-types in ia64_get_irr() md-raid10: fix KASAN warning serial: mvebu-uart: uart2 error bits clearing fuse: limit nsec iio: light: isl29028: Fix the warning in isl29028_remove() bpf: Verifer, adjust_scalar_min_max_vals to always call update_reg_bounds() drm/amdgpu: Check BO's requested pinning domains against its preferred_domains drm/nouveau: fix another off-by-one in nvbios_addr parisc: Fix device names in /proc/iomem ovl: drop WARN_ON() dentry is NULL in ovl_encode_fh() usbnet: Fix linkwatch use-after-free on disconnect fbcon: Fix boundary checks for fbcon=vc:n1-n2 parameters thermal: sysfs: Fix cooling_device_stats_setup() error code path fs: Add missing umask strip in vfs_tmpfile vfs: Check the truncate maximum size in inode_newsize_ok() tty: vt: initialize unicode screen buffer ALSA: hda/cirrus - support for iMac 12,1 model ALSA: hda/conexant: Add quirk for LENOVO 20149 Notebook model KVM: x86: Set error code to segment selector on LLDT/LTR non-canonical #GP KVM: x86: Mark TSS busy during LTR emulation _after_ all fault checks KVM: SVM: Don't BUG if userspace injects an interrupt with GIF=0 HID: wacom: Don't register pad_input for touch switch add barriers to buffer_uptodate and set_buffer_uptodate wifi: mac80211_hwsim: use 32-bit skb cookie wifi: mac80211_hwsim: add back erroneously removed cast wifi: mac80211_hwsim: fix race condition in pending packet ALSA: bcd2000: Fix a UAF bug on the error path of probing x86: link vdso and boot with -z noexecstack --no-warn-rwx-segments Makefile: link with -z noexecstack --no-warn-rwx-segments Conflicts: drivers/usb/dwc3/core.c Change-Id: I6a80af5bf9ef489c3a2be1d4f6f6589e26bb3102
1493 lines
38 KiB
C
1493 lines
38 KiB
C
/*
|
|
* Based on arch/arm/mm/mmu.c
|
|
*
|
|
* Copyright (C) 1995-2005 Russell King
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/cache.h>
|
|
#include <linux/export.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/kexec.h>
|
|
#include <linux/libfdt.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/nodemask.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/dma-contiguous.h>
|
|
#include <linux/cma.h>
|
|
|
|
#include <asm/barrier.h>
|
|
#include <asm/cputype.h>
|
|
#include <asm/fixmap.h>
|
|
#include <asm/kasan.h>
|
|
#include <asm/kernel-pgtable.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/sizes.h>
|
|
#include <asm/tlb.h>
|
|
#include <asm/memblock.h>
|
|
#include <asm/mmu_context.h>
|
|
#include <asm/ptdump.h>
|
|
#include <asm/tlbflush.h>
|
|
|
|
#define NO_BLOCK_MAPPINGS BIT(0)
|
|
#define NO_CONT_MAPPINGS BIT(1)
|
|
|
|
u64 idmap_t0sz = TCR_T0SZ(VA_BITS);
|
|
u64 idmap_ptrs_per_pgd = PTRS_PER_PGD;
|
|
|
|
u64 kimage_voffset __ro_after_init;
|
|
EXPORT_SYMBOL(kimage_voffset);
|
|
|
|
/*
|
|
* Empty_zero_page is a special page that is used for zero-initialized data
|
|
* and COW.
|
|
*/
|
|
unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss;
|
|
EXPORT_SYMBOL(empty_zero_page);
|
|
|
|
static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
|
|
static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused;
|
|
static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;
|
|
|
|
struct dma_contig_early_reserve {
|
|
phys_addr_t base;
|
|
unsigned long size;
|
|
};
|
|
|
|
static struct dma_contig_early_reserve dma_mmu_remap[MAX_CMA_AREAS];
|
|
static int dma_mmu_remap_num;
|
|
|
|
void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
|
|
{
|
|
if (dma_mmu_remap_num >= ARRAY_SIZE(dma_mmu_remap)) {
|
|
pr_err("ARM64: Not enough slots for DMA fixup reserved regions!\n");
|
|
return;
|
|
}
|
|
dma_mmu_remap[dma_mmu_remap_num].base = base;
|
|
dma_mmu_remap[dma_mmu_remap_num].size = size;
|
|
dma_mmu_remap_num++;
|
|
}
|
|
|
|
static bool dma_overlap(phys_addr_t start, phys_addr_t end)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < dma_mmu_remap_num; i++) {
|
|
phys_addr_t dma_base = dma_mmu_remap[i].base;
|
|
phys_addr_t dma_end = dma_mmu_remap[i].base +
|
|
dma_mmu_remap[i].size;
|
|
|
|
if ((dma_base < end) && (dma_end > start))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
|
|
unsigned long size, pgprot_t vma_prot)
|
|
{
|
|
if (!pfn_valid(pfn))
|
|
return pgprot_noncached(vma_prot);
|
|
else if (file->f_flags & O_SYNC)
|
|
return pgprot_writecombine(vma_prot);
|
|
return vma_prot;
|
|
}
|
|
EXPORT_SYMBOL(phys_mem_access_prot);
|
|
|
|
static phys_addr_t __init early_pgtable_alloc(void)
|
|
{
|
|
phys_addr_t phys;
|
|
void *ptr;
|
|
|
|
phys = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
|
|
|
|
/*
|
|
* The FIX_{PGD,PUD,PMD} slots may be in active use, but the FIX_PTE
|
|
* slot will be free, so we can (ab)use the FIX_PTE slot to initialise
|
|
* any level of table.
|
|
*/
|
|
ptr = pte_set_fixmap(phys);
|
|
|
|
memset(ptr, 0, PAGE_SIZE);
|
|
|
|
/*
|
|
* Implicit barriers also ensure the zeroed page is visible to the page
|
|
* table walker
|
|
*/
|
|
pte_clear_fixmap();
|
|
|
|
return phys;
|
|
}
|
|
|
|
static bool pgattr_change_is_safe(u64 old, u64 new)
|
|
{
|
|
/*
|
|
* The following mapping attributes may be updated in live
|
|
* kernel mappings without the need for break-before-make.
|
|
*/
|
|
static const pteval_t mask = PTE_PXN | PTE_RDONLY | PTE_WRITE | PTE_NG;
|
|
|
|
/* creating or taking down mappings is always safe */
|
|
if (old == 0 || new == 0)
|
|
return true;
|
|
|
|
/* live contiguous mappings may not be manipulated at all */
|
|
if ((old | new) & PTE_CONT)
|
|
return false;
|
|
|
|
/* Transitioning from Non-Global to Global is unsafe */
|
|
if (old & ~new & PTE_NG)
|
|
return false;
|
|
|
|
return ((old ^ new) & ~mask) == 0;
|
|
}
|
|
|
|
static void init_pte(pmd_t *pmdp, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys, pgprot_t prot)
|
|
{
|
|
pte_t *ptep;
|
|
|
|
ptep = pte_set_fixmap_offset(pmdp, addr);
|
|
do {
|
|
pte_t old_pte = READ_ONCE(*ptep);
|
|
|
|
set_pte(ptep, pfn_pte(__phys_to_pfn(phys), prot));
|
|
|
|
/*
|
|
* After the PTE entry has been populated once, we
|
|
* only allow updates to the permission attributes.
|
|
*/
|
|
BUG_ON(!pgattr_change_is_safe(pte_val(old_pte),
|
|
READ_ONCE(pte_val(*ptep))));
|
|
|
|
phys += PAGE_SIZE;
|
|
} while (ptep++, addr += PAGE_SIZE, addr != end);
|
|
|
|
pte_clear_fixmap();
|
|
}
|
|
|
|
static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
|
|
unsigned long end, phys_addr_t phys,
|
|
pgprot_t prot,
|
|
phys_addr_t (*pgtable_alloc)(void),
|
|
int flags)
|
|
{
|
|
unsigned long next;
|
|
pmd_t pmd = READ_ONCE(*pmdp);
|
|
|
|
BUG_ON(pmd_sect(pmd));
|
|
if (pmd_none(pmd)) {
|
|
phys_addr_t pte_phys;
|
|
BUG_ON(!pgtable_alloc);
|
|
pte_phys = pgtable_alloc();
|
|
__pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
|
|
pmd = READ_ONCE(*pmdp);
|
|
}
|
|
BUG_ON(pmd_bad(pmd));
|
|
|
|
do {
|
|
pgprot_t __prot = prot;
|
|
|
|
next = pte_cont_addr_end(addr, end);
|
|
|
|
/* use a contiguous mapping if the range is suitably aligned */
|
|
if ((((addr | next | phys) & ~CONT_PTE_MASK) == 0) &&
|
|
(flags & NO_CONT_MAPPINGS) == 0)
|
|
__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
|
|
|
|
init_pte(pmdp, addr, next, phys, __prot);
|
|
|
|
phys += next - addr;
|
|
} while (addr = next, addr != end);
|
|
}
|
|
|
|
static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys, pgprot_t prot,
|
|
phys_addr_t (*pgtable_alloc)(void), int flags)
|
|
{
|
|
unsigned long next;
|
|
pmd_t *pmdp;
|
|
|
|
pmdp = pmd_set_fixmap_offset(pudp, addr);
|
|
do {
|
|
pmd_t old_pmd = READ_ONCE(*pmdp);
|
|
|
|
next = pmd_addr_end(addr, end);
|
|
|
|
/* try section mapping first */
|
|
if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
|
|
(flags & NO_BLOCK_MAPPINGS) == 0 &&
|
|
!dma_overlap(phys, phys + next - addr)) {
|
|
pmd_set_huge(pmdp, phys, prot);
|
|
|
|
/*
|
|
* After the PMD entry has been populated once, we
|
|
* only allow updates to the permission attributes.
|
|
*/
|
|
BUG_ON(!pgattr_change_is_safe(pmd_val(old_pmd),
|
|
READ_ONCE(pmd_val(*pmdp))));
|
|
} else {
|
|
alloc_init_cont_pte(pmdp, addr, next, phys, prot,
|
|
pgtable_alloc, flags);
|
|
|
|
BUG_ON(pmd_val(old_pmd) != 0 &&
|
|
pmd_val(old_pmd) != READ_ONCE(pmd_val(*pmdp)));
|
|
}
|
|
phys += next - addr;
|
|
} while (pmdp++, addr = next, addr != end);
|
|
|
|
pmd_clear_fixmap();
|
|
}
|
|
|
|
static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
|
|
unsigned long end, phys_addr_t phys,
|
|
pgprot_t prot,
|
|
phys_addr_t (*pgtable_alloc)(void), int flags)
|
|
{
|
|
unsigned long next;
|
|
pud_t pud = READ_ONCE(*pudp);
|
|
|
|
/*
|
|
* Check for initial section mappings in the pgd/pud.
|
|
*/
|
|
BUG_ON(pud_sect(pud));
|
|
if (pud_none(pud)) {
|
|
phys_addr_t pmd_phys;
|
|
BUG_ON(!pgtable_alloc);
|
|
pmd_phys = pgtable_alloc();
|
|
__pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE);
|
|
pud = READ_ONCE(*pudp);
|
|
}
|
|
BUG_ON(pud_bad(pud));
|
|
|
|
do {
|
|
pgprot_t __prot = prot;
|
|
|
|
next = pmd_cont_addr_end(addr, end);
|
|
|
|
/* use a contiguous mapping if the range is suitably aligned */
|
|
if ((((addr | next | phys) & ~CONT_PMD_MASK) == 0) &&
|
|
(flags & NO_CONT_MAPPINGS) == 0)
|
|
__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
|
|
|
|
init_pmd(pudp, addr, next, phys, __prot, pgtable_alloc, flags);
|
|
|
|
phys += next - addr;
|
|
} while (addr = next, addr != end);
|
|
}
|
|
|
|
static inline bool use_1G_block(unsigned long addr, unsigned long next,
|
|
unsigned long phys)
|
|
{
|
|
if (PAGE_SHIFT != 12)
|
|
return false;
|
|
|
|
if (((addr | next | phys) & ~PUD_MASK) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys, pgprot_t prot,
|
|
phys_addr_t (*pgtable_alloc)(void),
|
|
int flags)
|
|
{
|
|
unsigned long next;
|
|
pud_t *pudp;
|
|
pgd_t pgd = READ_ONCE(*pgdp);
|
|
|
|
if (pgd_none(pgd)) {
|
|
phys_addr_t pud_phys;
|
|
BUG_ON(!pgtable_alloc);
|
|
pud_phys = pgtable_alloc();
|
|
__pgd_populate(pgdp, pud_phys, PUD_TYPE_TABLE);
|
|
pgd = READ_ONCE(*pgdp);
|
|
}
|
|
BUG_ON(pgd_bad(pgd));
|
|
|
|
pudp = pud_set_fixmap_offset(pgdp, addr);
|
|
do {
|
|
pud_t old_pud = READ_ONCE(*pudp);
|
|
|
|
next = pud_addr_end(addr, end);
|
|
|
|
/*
|
|
* For 4K granule only, attempt to put down a 1GB block
|
|
*/
|
|
if (use_1G_block(addr, next, phys) &&
|
|
(flags & NO_BLOCK_MAPPINGS) == 0 &&
|
|
!dma_overlap(phys, phys + next - addr)) {
|
|
pud_set_huge(pudp, phys, prot);
|
|
|
|
/*
|
|
* After the PUD entry has been populated once, we
|
|
* only allow updates to the permission attributes.
|
|
*/
|
|
BUG_ON(!pgattr_change_is_safe(pud_val(old_pud),
|
|
READ_ONCE(pud_val(*pudp))));
|
|
} else {
|
|
alloc_init_cont_pmd(pudp, addr, next, phys, prot,
|
|
pgtable_alloc, flags);
|
|
|
|
BUG_ON(pud_val(old_pud) != 0 &&
|
|
pud_val(old_pud) != READ_ONCE(pud_val(*pudp)));
|
|
}
|
|
phys += next - addr;
|
|
} while (pudp++, addr = next, addr != end);
|
|
|
|
pud_clear_fixmap();
|
|
}
|
|
|
|
static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
|
|
unsigned long virt, phys_addr_t size,
|
|
pgprot_t prot,
|
|
phys_addr_t (*pgtable_alloc)(void),
|
|
int flags)
|
|
{
|
|
unsigned long addr, length, end, next;
|
|
pgd_t *pgdp = pgd_offset_raw(pgdir, virt);
|
|
|
|
/*
|
|
* If the virtual and physical address don't have the same offset
|
|
* within a page, we cannot map the region as the caller expects.
|
|
*/
|
|
if (WARN_ON((phys ^ virt) & ~PAGE_MASK))
|
|
return;
|
|
|
|
phys &= PAGE_MASK;
|
|
addr = virt & PAGE_MASK;
|
|
length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));
|
|
|
|
end = addr + length;
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
alloc_init_pud(pgdp, addr, next, phys, prot, pgtable_alloc,
|
|
flags);
|
|
phys += next - addr;
|
|
} while (pgdp++, addr = next, addr != end);
|
|
}
|
|
|
|
static phys_addr_t pgd_pgtable_alloc(void)
|
|
{
|
|
void *ptr = (void *)__get_free_page(PGALLOC_GFP);
|
|
if (!ptr || !pgtable_page_ctor(virt_to_page(ptr)))
|
|
BUG();
|
|
|
|
/* Ensure the zeroed page is visible to the page table walker */
|
|
dsb(ishst);
|
|
return __pa(ptr);
|
|
}
|
|
|
|
/**
|
|
* create_pgtable_mapping - create a pagetable mapping for given
|
|
* physical start and end addresses.
|
|
* @start: physical start address.
|
|
* @end: physical end address.
|
|
*/
|
|
void create_pgtable_mapping(phys_addr_t start, phys_addr_t end)
|
|
{
|
|
unsigned long virt = (unsigned long)phys_to_virt(start);
|
|
|
|
if (virt < VMALLOC_START) {
|
|
pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n",
|
|
&start, virt);
|
|
return;
|
|
}
|
|
|
|
__create_pgd_mapping(init_mm.pgd, start, virt, end - start,
|
|
PAGE_KERNEL, NULL, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(create_pgtable_mapping);
|
|
|
|
/*
|
|
* This function can only be used to modify existing table entries,
|
|
* without allocating new levels of table. Note that this permits the
|
|
* creation of new section or page entries.
|
|
*/
|
|
static void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
|
|
phys_addr_t size, pgprot_t prot)
|
|
{
|
|
if (virt < VMALLOC_START) {
|
|
pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n",
|
|
&phys, virt);
|
|
return;
|
|
}
|
|
__create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
|
|
NO_CONT_MAPPINGS);
|
|
}
|
|
|
|
void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
|
|
unsigned long virt, phys_addr_t size,
|
|
pgprot_t prot, bool page_mappings_only)
|
|
{
|
|
int flags = 0;
|
|
|
|
BUG_ON(mm == &init_mm);
|
|
|
|
if (page_mappings_only)
|
|
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
|
|
|
__create_pgd_mapping(mm->pgd, phys, virt, size, prot,
|
|
pgd_pgtable_alloc, flags);
|
|
}
|
|
|
|
static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
|
|
phys_addr_t size, pgprot_t prot)
|
|
{
|
|
if (virt < VMALLOC_START) {
|
|
pr_warn("BUG: not updating mapping for %pa at 0x%016lx - outside kernel range\n",
|
|
&phys, virt);
|
|
return;
|
|
}
|
|
|
|
__create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
|
|
NO_CONT_MAPPINGS);
|
|
|
|
/* flush the TLBs after updating live kernel mappings */
|
|
flush_tlb_kernel_range(virt, virt + size);
|
|
}
|
|
|
|
static void __init __map_memblock(pgd_t *pgdp, phys_addr_t start,
|
|
phys_addr_t end, pgprot_t prot, int flags)
|
|
{
|
|
__create_pgd_mapping(pgdp, start, __phys_to_virt(start), end - start,
|
|
prot, early_pgtable_alloc, flags);
|
|
}
|
|
|
|
void __init mark_linear_text_alias_ro(void)
|
|
{
|
|
/*
|
|
* Remove the write permissions from the linear alias of .text/.rodata
|
|
*/
|
|
update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
|
|
(unsigned long)__init_begin - (unsigned long)_text,
|
|
PAGE_KERNEL_RO);
|
|
}
|
|
|
|
static void __init map_mem(pgd_t *pgdp)
|
|
{
|
|
phys_addr_t kernel_start = __pa_symbol(_text);
|
|
phys_addr_t kernel_end = __pa_symbol(__init_begin);
|
|
struct memblock_region *reg;
|
|
int flags = 0;
|
|
|
|
if (debug_pagealloc_enabled())
|
|
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
|
|
|
/*
|
|
* Take care not to create a writable alias for the
|
|
* read-only text and rodata sections of the kernel image.
|
|
* So temporarily mark them as NOMAP to skip mappings in
|
|
* the following for-loop
|
|
*/
|
|
memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
|
|
#ifdef CONFIG_KEXEC_CORE
|
|
if (crashk_res.end)
|
|
memblock_mark_nomap(crashk_res.start,
|
|
resource_size(&crashk_res));
|
|
#endif
|
|
|
|
/* map all the memory banks */
|
|
for_each_memblock(memory, reg) {
|
|
phys_addr_t start = reg->base;
|
|
phys_addr_t end = start + reg->size;
|
|
|
|
if (start >= end)
|
|
break;
|
|
if (memblock_is_nomap(reg))
|
|
continue;
|
|
|
|
__map_memblock(pgdp, start, end, PAGE_KERNEL, flags);
|
|
}
|
|
|
|
/*
|
|
* Map the linear alias of the [_text, __init_begin) interval
|
|
* as non-executable now, and remove the write permission in
|
|
* mark_linear_text_alias_ro() below (which will be called after
|
|
* alternative patching has completed). This makes the contents
|
|
* of the region accessible to subsystems such as hibernate,
|
|
* but protects it from inadvertent modification or execution.
|
|
* Note that contiguous mappings cannot be remapped in this way,
|
|
* so we should avoid them here.
|
|
*/
|
|
__map_memblock(pgdp, kernel_start, kernel_end,
|
|
PAGE_KERNEL, NO_CONT_MAPPINGS);
|
|
memblock_clear_nomap(kernel_start, kernel_end - kernel_start);
|
|
|
|
#ifdef CONFIG_KEXEC_CORE
|
|
/*
|
|
* Use page-level mappings here so that we can shrink the region
|
|
* in page granularity and put back unused memory to buddy system
|
|
* through /sys/kernel/kexec_crash_size interface.
|
|
*/
|
|
if (crashk_res.end) {
|
|
__map_memblock(pgdp, crashk_res.start, crashk_res.end + 1,
|
|
PAGE_KERNEL,
|
|
NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS);
|
|
memblock_clear_nomap(crashk_res.start,
|
|
resource_size(&crashk_res));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void mark_rodata_ro(void)
|
|
{
|
|
unsigned long section_size;
|
|
|
|
/*
|
|
* mark .rodata as read only. Use __init_begin rather than __end_rodata
|
|
* to cover NOTES and EXCEPTION_TABLE.
|
|
*/
|
|
section_size = (unsigned long)__init_begin - (unsigned long)__start_rodata;
|
|
update_mapping_prot(__pa_symbol(__start_rodata), (unsigned long)__start_rodata,
|
|
section_size, PAGE_KERNEL_RO);
|
|
|
|
debug_checkwx();
|
|
}
|
|
|
|
static void __init map_kernel_segment(pgd_t *pgdp, void *va_start, void *va_end,
|
|
pgprot_t prot, struct vm_struct *vma,
|
|
int flags, unsigned long vm_flags)
|
|
{
|
|
phys_addr_t pa_start = __pa_symbol(va_start);
|
|
unsigned long size = va_end - va_start;
|
|
|
|
BUG_ON(!PAGE_ALIGNED(pa_start));
|
|
BUG_ON(!PAGE_ALIGNED(size));
|
|
|
|
__create_pgd_mapping(pgdp, pa_start, (unsigned long)va_start, size, prot,
|
|
early_pgtable_alloc, flags);
|
|
|
|
if (!(vm_flags & VM_NO_GUARD))
|
|
size += PAGE_SIZE;
|
|
|
|
vma->addr = va_start;
|
|
vma->phys_addr = pa_start;
|
|
vma->size = size;
|
|
vma->flags = VM_MAP | vm_flags;
|
|
vma->caller = __builtin_return_address(0);
|
|
|
|
vm_area_add_early(vma);
|
|
}
|
|
|
|
static int __init parse_rodata(char *arg)
|
|
{
|
|
return strtobool(arg, &rodata_enabled);
|
|
}
|
|
early_param("rodata", parse_rodata);
|
|
|
|
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
|
|
static int __init map_entry_trampoline(void)
|
|
{
|
|
int i;
|
|
|
|
pgprot_t prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
|
|
phys_addr_t pa_start = __pa_symbol(__entry_tramp_text_start);
|
|
|
|
/* The trampoline is always mapped and can therefore be global */
|
|
pgprot_val(prot) &= ~PTE_NG;
|
|
|
|
/* Map only the text into the trampoline page table */
|
|
memset(tramp_pg_dir, 0, PGD_SIZE);
|
|
__create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS,
|
|
entry_tramp_text_size(), prot, pgd_pgtable_alloc,
|
|
0);
|
|
|
|
/* Map both the text and data into the kernel page table */
|
|
for (i = 0; i < DIV_ROUND_UP(entry_tramp_text_size(), PAGE_SIZE); i++)
|
|
__set_fixmap(FIX_ENTRY_TRAMP_TEXT1 - i,
|
|
pa_start + i * PAGE_SIZE, prot);
|
|
|
|
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
|
|
extern char __entry_tramp_data_start[];
|
|
|
|
__set_fixmap(FIX_ENTRY_TRAMP_DATA,
|
|
__pa_symbol(__entry_tramp_data_start),
|
|
PAGE_KERNEL_RO);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
core_initcall(map_entry_trampoline);
|
|
#endif
|
|
|
|
/*
|
|
* Create fine-grained mappings for the kernel.
|
|
*/
|
|
static void __init map_kernel(pgd_t *pgdp)
|
|
{
|
|
static struct vm_struct vmlinux_text, vmlinux_rodata, vmlinux_inittext,
|
|
vmlinux_initdata, vmlinux_data;
|
|
|
|
/*
|
|
* External debuggers may need to write directly to the text
|
|
* mapping to install SW breakpoints. Allow this (only) when
|
|
* explicitly requested with rodata=off.
|
|
*/
|
|
pgprot_t text_prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
|
|
|
|
/*
|
|
* Only rodata will be remapped with different permissions later on,
|
|
* all other segments are allowed to use contiguous mappings.
|
|
*/
|
|
map_kernel_segment(pgdp, _text, _etext, text_prot, &vmlinux_text, 0,
|
|
VM_NO_GUARD);
|
|
map_kernel_segment(pgdp, __start_rodata, __inittext_begin, PAGE_KERNEL,
|
|
&vmlinux_rodata, NO_CONT_MAPPINGS, VM_NO_GUARD);
|
|
map_kernel_segment(pgdp, __inittext_begin, __inittext_end, text_prot,
|
|
&vmlinux_inittext, 0, VM_NO_GUARD);
|
|
map_kernel_segment(pgdp, __initdata_begin, __initdata_end, PAGE_KERNEL,
|
|
&vmlinux_initdata, 0, VM_NO_GUARD);
|
|
map_kernel_segment(pgdp, _data, _end, PAGE_KERNEL, &vmlinux_data, 0, 0);
|
|
|
|
if (!READ_ONCE(pgd_val(*pgd_offset_raw(pgdp, FIXADDR_START)))) {
|
|
/*
|
|
* The fixmap falls in a separate pgd to the kernel, and doesn't
|
|
* live in the carveout for the swapper_pg_dir. We can simply
|
|
* re-use the existing dir for the fixmap.
|
|
*/
|
|
set_pgd(pgd_offset_raw(pgdp, FIXADDR_START),
|
|
READ_ONCE(*pgd_offset_k(FIXADDR_START)));
|
|
} else if (CONFIG_PGTABLE_LEVELS > 3) {
|
|
/*
|
|
* The fixmap shares its top level pgd entry with the kernel
|
|
* mapping. This can really only occur when we are running
|
|
* with 16k/4 levels, so we can simply reuse the pud level
|
|
* entry instead.
|
|
*/
|
|
BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
|
|
pud_populate(&init_mm,
|
|
pud_set_fixmap_offset(pgdp, FIXADDR_START),
|
|
lm_alias(bm_pmd));
|
|
pud_clear_fixmap();
|
|
} else {
|
|
BUG();
|
|
}
|
|
|
|
kasan_copy_shadow(pgdp);
|
|
}
|
|
|
|
/*
|
|
* paging_init() sets up the page tables, initialises the zone memory
|
|
* maps and sets up the zero page.
|
|
*/
|
|
void __init paging_init(void)
|
|
{
|
|
phys_addr_t pgd_phys = early_pgtable_alloc();
|
|
pgd_t *pgdp = pgd_set_fixmap(pgd_phys);
|
|
|
|
map_kernel(pgdp);
|
|
map_mem(pgdp);
|
|
|
|
/*
|
|
* We want to reuse the original swapper_pg_dir so we don't have to
|
|
* communicate the new address to non-coherent secondaries in
|
|
* secondary_entry, and so cpu_switch_mm can generate the address with
|
|
* adrp+add rather than a load from some global variable.
|
|
*
|
|
* To do this we need to go via a temporary pgd.
|
|
*/
|
|
cpu_replace_ttbr1(__va(pgd_phys));
|
|
memcpy(swapper_pg_dir, pgdp, PGD_SIZE);
|
|
cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
|
|
|
|
pgd_clear_fixmap();
|
|
memblock_free(pgd_phys, PAGE_SIZE);
|
|
|
|
/*
|
|
* We only reuse the PGD from the swapper_pg_dir, not the pud + pmd
|
|
* allocated with it.
|
|
*/
|
|
memblock_free(__pa_symbol(swapper_pg_dir) + PAGE_SIZE,
|
|
__pa_symbol(swapper_pg_end) - __pa_symbol(swapper_pg_dir)
|
|
- PAGE_SIZE);
|
|
}
|
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
/*
|
|
* hotplug_paging() is used by memory hotplug to build new page tables
|
|
* for hot added memory.
|
|
*/
|
|
void hotplug_paging(phys_addr_t start, phys_addr_t size)
|
|
{
|
|
int flags;
|
|
|
|
flags = debug_pagealloc_enabled() ? NO_BLOCK_MAPPINGS : 0;
|
|
__create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start), size,
|
|
PAGE_KERNEL, pgd_pgtable_alloc, flags);
|
|
}
|
|
|
|
#ifdef CONFIG_MEMORY_HOTREMOVE
|
|
#define PAGE_INUSE 0xFD
|
|
|
|
static void free_pagetable(struct page *page, int order, bool direct)
|
|
{
|
|
unsigned long magic;
|
|
unsigned int nr_pages = 1 << order;
|
|
|
|
/* bootmem page has reserved flag */
|
|
if (PageReserved(page)) {
|
|
__ClearPageReserved(page);
|
|
|
|
magic = (unsigned long)page->lru.next;
|
|
if (magic == SECTION_INFO || magic == MIX_SECTION_INFO) {
|
|
while (nr_pages--)
|
|
put_page_bootmem(page++);
|
|
} else {
|
|
while (nr_pages--)
|
|
free_reserved_page(page++);
|
|
}
|
|
} else {
|
|
/*
|
|
* Only direct pagetable allocation (those allocated via
|
|
* hotplug) call the pgtable_page_ctor; vmemmap pgtable
|
|
* allocations don't.
|
|
*/
|
|
if (direct)
|
|
pgtable_page_dtor(page);
|
|
|
|
free_pages((unsigned long)page_address(page), order);
|
|
}
|
|
}
|
|
|
|
static void free_pte_table(pmd_t *pmd, bool direct)
|
|
{
|
|
pte_t *pte_start, *pte;
|
|
struct page *page;
|
|
int i;
|
|
|
|
pte_start = (pte_t *) pmd_page_vaddr(*pmd);
|
|
/* Check if there is no valid entry in the PMD */
|
|
for (i = 0; i < PTRS_PER_PTE; i++) {
|
|
pte = pte_start + i;
|
|
if (!pte_none(*pte))
|
|
return;
|
|
}
|
|
|
|
page = pmd_page(*pmd);
|
|
|
|
free_pagetable(page, 0, direct);
|
|
|
|
/*
|
|
* This spin lock could be only taken in _pte_aloc_kernel
|
|
* in mm/memory.c and nowhere else (for arm64). Not sure if
|
|
* the function above can be called concurrently. In doubt,
|
|
* I am living it here for now, but it probably can be removed
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pmd_clear(pmd);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
|
|
static void free_pmd_table(pud_t *pud, bool direct)
|
|
{
|
|
pmd_t *pmd_start, *pmd;
|
|
struct page *page;
|
|
int i;
|
|
|
|
pmd_start = (pmd_t *) pud_page_vaddr(*pud);
|
|
/* Check if there is no valid entry in the PMD */
|
|
for (i = 0; i < PTRS_PER_PMD; i++) {
|
|
pmd = pmd_start + i;
|
|
if (!pmd_none(*pmd))
|
|
return;
|
|
}
|
|
|
|
page = pud_page(*pud);
|
|
|
|
free_pagetable(page, 0, direct);
|
|
|
|
/*
|
|
* This spin lock could be only taken in _pte_aloc_kernel
|
|
* in mm/memory.c and nowhere else (for arm64). Not sure if
|
|
* the function above can be called concurrently. In doubt,
|
|
* I am living it here for now, but it probably can be removed
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pud_clear(pud);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
|
|
/*
|
|
* When the PUD is folded on the PGD (three levels of paging),
|
|
* there's no need to free PUDs
|
|
*/
|
|
#if CONFIG_PGTABLE_LEVELS > 3
|
|
static void free_pud_table(pgd_t *pgd, bool direct)
|
|
{
|
|
pud_t *pud_start, *pud;
|
|
struct page *page;
|
|
int i;
|
|
|
|
pud_start = (pud_t *) pgd_page_vaddr(*pgd);
|
|
/* Check if there is no valid entry in the PUD */
|
|
for (i = 0; i < PTRS_PER_PUD; i++) {
|
|
pud = pud_start + i;
|
|
if (!pud_none(*pud))
|
|
return;
|
|
}
|
|
|
|
page = pgd_page(*pgd);
|
|
|
|
free_pagetable(page, 0, direct);
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pgd_clear(pgd);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
#endif
|
|
|
|
static void remove_pte_table(pte_t *pte, unsigned long addr,
|
|
unsigned long end, bool direct)
|
|
{
|
|
unsigned long next;
|
|
void *page_addr;
|
|
|
|
for (; addr < end; addr = next, pte++) {
|
|
next = (addr + PAGE_SIZE) & PAGE_MASK;
|
|
if (next > end)
|
|
next = end;
|
|
|
|
if (!pte_present(*pte))
|
|
continue;
|
|
|
|
if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) {
|
|
/*
|
|
* Do not free direct mapping pages since they were
|
|
* freed when offlining, or simplely not in use.
|
|
*/
|
|
if (!direct)
|
|
free_pagetable(pte_page(*pte), 0, direct);
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pte_clear(&init_mm, addr, pte);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
} else {
|
|
/*
|
|
* If we are here, we are freeing vmemmap pages since
|
|
* direct mapped memory ranges to be freed are aligned.
|
|
*
|
|
* If we are not removing the whole page, it means
|
|
* other page structs in this page are being used and
|
|
* we canot remove them. So fill the unused page_structs
|
|
* with 0xFD, and remove the page when it is wholly
|
|
* filled with 0xFD.
|
|
*/
|
|
memset((void *)addr, PAGE_INUSE, next - addr);
|
|
|
|
page_addr = page_address(pte_page(*pte));
|
|
if (!memchr_inv(page_addr, PAGE_INUSE, PAGE_SIZE)) {
|
|
free_pagetable(pte_page(*pte), 0, direct);
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pte_clear(&init_mm, addr, pte);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
// I am adding this flush here in simmetry to the x86 code.
|
|
// Why do I need to call it here and not in remove_p[mu]d
|
|
flush_tlb_all();
|
|
}
|
|
|
|
static void remove_pmd_table(pmd_t *pmd, unsigned long addr,
|
|
unsigned long end, bool direct)
|
|
{
|
|
unsigned long next;
|
|
void *page_addr;
|
|
pte_t *pte;
|
|
|
|
for (; addr < end; addr = next, pmd++) {
|
|
next = pmd_addr_end(addr, end);
|
|
|
|
if (!pmd_present(*pmd))
|
|
continue;
|
|
|
|
// check if we are using 2MB section mappings
|
|
if (pmd_sect(*pmd)) {
|
|
if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) {
|
|
if (!direct) {
|
|
free_pagetable(pmd_page(*pmd),
|
|
get_order(PMD_SIZE), direct);
|
|
}
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pmd_clear(pmd);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
} else {
|
|
/* If here, we are freeing vmemmap pages. */
|
|
memset((void *)addr, PAGE_INUSE, next - addr);
|
|
|
|
page_addr = page_address(pmd_page(*pmd));
|
|
if (!memchr_inv(page_addr, PAGE_INUSE,
|
|
PMD_SIZE)) {
|
|
free_pagetable(pmd_page(*pmd),
|
|
get_order(PMD_SIZE), direct);
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pmd_clear(pmd);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
BUG_ON(!pmd_table(*pmd));
|
|
|
|
pte = pte_offset_map(pmd, addr);
|
|
remove_pte_table(pte, addr, next, direct);
|
|
free_pte_table(pmd, direct);
|
|
}
|
|
}
|
|
|
|
static void remove_pud_table(pud_t *pud, unsigned long addr,
|
|
unsigned long end, bool direct)
|
|
{
|
|
unsigned long next;
|
|
pmd_t *pmd;
|
|
void *page_addr;
|
|
|
|
for (; addr < end; addr = next, pud++) {
|
|
next = pud_addr_end(addr, end);
|
|
if (!pud_present(*pud))
|
|
continue;
|
|
/*
|
|
* If we are using 4K granules, check if we are using
|
|
* 1GB section mapping.
|
|
*/
|
|
if (pud_sect(*pud)) {
|
|
if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) {
|
|
if (!direct) {
|
|
free_pagetable(pud_page(*pud),
|
|
get_order(PUD_SIZE), direct);
|
|
}
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pud_clear(pud);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
} else {
|
|
/* If here, we are freeing vmemmap pages. */
|
|
memset((void *)addr, PAGE_INUSE, next - addr);
|
|
|
|
page_addr = page_address(pud_page(*pud));
|
|
if (!memchr_inv(page_addr, PAGE_INUSE,
|
|
PUD_SIZE)) {
|
|
|
|
free_pagetable(pud_page(*pud),
|
|
get_order(PUD_SIZE), direct);
|
|
|
|
/*
|
|
* This spin lock could be only
|
|
* taken in _pte_aloc_kernel in
|
|
* mm/memory.c and nowhere else
|
|
* (for arm64). Not sure if the
|
|
* function above can be called
|
|
* concurrently. In doubt,
|
|
* I am living it here for now,
|
|
* but it probably can be removed.
|
|
*/
|
|
spin_lock(&init_mm.page_table_lock);
|
|
pud_clear(pud);
|
|
spin_unlock(&init_mm.page_table_lock);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
BUG_ON(!pud_table(*pud));
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
remove_pmd_table(pmd, addr, next, direct);
|
|
free_pmd_table(pud, direct);
|
|
}
|
|
}
|
|
|
|
void remove_pagetable(unsigned long start, unsigned long end, bool direct)
|
|
{
|
|
unsigned long next;
|
|
unsigned long addr;
|
|
pgd_t *pgd;
|
|
pud_t *pud;
|
|
|
|
for (addr = start; addr < end; addr = next) {
|
|
next = pgd_addr_end(addr, end);
|
|
|
|
pgd = pgd_offset_k(addr);
|
|
if (pgd_none(*pgd))
|
|
continue;
|
|
|
|
pud = pud_offset(pgd, addr);
|
|
remove_pud_table(pud, addr, next, direct);
|
|
/*
|
|
* When the PUD is folded on the PGD (three levels of paging),
|
|
* I did already clear the PMD page in free_pmd_table,
|
|
* and reset the corresponding PGD==PUD entry.
|
|
*/
|
|
#if CONFIG_PGTABLE_LEVELS > 3
|
|
free_pud_table(pgd, direct);
|
|
#endif
|
|
}
|
|
|
|
flush_tlb_all();
|
|
}
|
|
|
|
|
|
#endif /* CONFIG_MEMORY_HOTREMOVE */
|
|
#endif /* CONFIG_MEMORY_HOTPLUG */
|
|
|
|
/*
|
|
* Check whether a kernel address is valid (derived from arch/x86/).
|
|
*/
|
|
int kern_addr_valid(unsigned long addr)
|
|
{
|
|
pgd_t *pgdp;
|
|
pud_t *pudp, pud;
|
|
pmd_t *pmdp, pmd;
|
|
pte_t *ptep, pte;
|
|
|
|
if ((((long)addr) >> VA_BITS) != -1UL)
|
|
return 0;
|
|
|
|
pgdp = pgd_offset_k(addr);
|
|
if (pgd_none(READ_ONCE(*pgdp)))
|
|
return 0;
|
|
|
|
pudp = pud_offset(pgdp, addr);
|
|
pud = READ_ONCE(*pudp);
|
|
if (pud_none(pud))
|
|
return 0;
|
|
|
|
if (pud_sect(pud))
|
|
return pfn_valid(pud_pfn(pud));
|
|
|
|
pmdp = pmd_offset(pudp, addr);
|
|
pmd = READ_ONCE(*pmdp);
|
|
if (pmd_none(pmd))
|
|
return 0;
|
|
|
|
if (pmd_sect(pmd))
|
|
return pfn_valid(pmd_pfn(pmd));
|
|
|
|
ptep = pte_offset_kernel(pmdp, addr);
|
|
pte = READ_ONCE(*ptep);
|
|
if (pte_none(pte))
|
|
return 0;
|
|
|
|
return pfn_valid(pte_pfn(pte));
|
|
}
|
|
EXPORT_SYMBOL_GPL(kern_addr_valid);
|
|
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
|
#if !ARM64_SWAPPER_USES_SECTION_MAPS
|
|
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
|
|
struct vmem_altmap *altmap)
|
|
{
|
|
return vmemmap_populate_basepages(start, end, node);
|
|
}
|
|
#else /* !ARM64_SWAPPER_USES_SECTION_MAPS */
|
|
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
|
|
struct vmem_altmap *altmap)
|
|
{
|
|
unsigned long addr = start;
|
|
unsigned long next;
|
|
pgd_t *pgdp;
|
|
pud_t *pudp;
|
|
pmd_t *pmdp;
|
|
int ret = 0;
|
|
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
|
|
pgdp = vmemmap_pgd_populate(addr, node);
|
|
if (!pgdp)
|
|
return -ENOMEM;
|
|
|
|
pudp = vmemmap_pud_populate(pgdp, addr, node);
|
|
if (!pudp)
|
|
return -ENOMEM;
|
|
|
|
pmdp = pmd_offset(pudp, addr);
|
|
if (pmd_none(READ_ONCE(*pmdp))) {
|
|
void *p = NULL;
|
|
|
|
p = vmemmap_alloc_block_buf(PMD_SIZE, node);
|
|
if (!p) {
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
vmemmap_free(start, end, altmap);
|
|
#endif
|
|
ret = -ENOMEM;
|
|
break;
|
|
}
|
|
|
|
pmd_set_huge(pmdp, __pa(p), __pgprot(PROT_SECT_NORMAL));
|
|
} else
|
|
vmemmap_verify((pte_t *)pmdp, node, addr, next);
|
|
} while (addr = next, addr != end);
|
|
|
|
if (ret)
|
|
return vmemmap_populate_basepages(start, end, node);
|
|
else
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_ARM64_64K_PAGES */
|
|
void vmemmap_free(unsigned long start, unsigned long end,
|
|
struct vmem_altmap *altmap)
|
|
{
|
|
#ifdef CONFIG_MEMORY_HOTREMOVE
|
|
remove_pagetable(start, end, false);
|
|
#endif
|
|
}
|
|
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
|
|
|
|
static inline pud_t * fixmap_pud(unsigned long addr)
|
|
{
|
|
pgd_t *pgdp = pgd_offset_k(addr);
|
|
pgd_t pgd = READ_ONCE(*pgdp);
|
|
|
|
BUG_ON(pgd_none(pgd) || pgd_bad(pgd));
|
|
|
|
return pud_offset_kimg(pgdp, addr);
|
|
}
|
|
|
|
static inline pmd_t * fixmap_pmd(unsigned long addr)
|
|
{
|
|
pud_t *pudp = fixmap_pud(addr);
|
|
pud_t pud = READ_ONCE(*pudp);
|
|
|
|
BUG_ON(pud_none(pud) || pud_bad(pud));
|
|
|
|
return pmd_offset_kimg(pudp, addr);
|
|
}
|
|
|
|
static inline pte_t * fixmap_pte(unsigned long addr)
|
|
{
|
|
return &bm_pte[pte_index(addr)];
|
|
}
|
|
|
|
/*
|
|
* The p*d_populate functions call virt_to_phys implicitly so they can't be used
|
|
* directly on kernel symbols (bm_p*d). This function is called too early to use
|
|
* lm_alias so __p*d_populate functions must be used to populate with the
|
|
* physical address from __pa_symbol.
|
|
*/
|
|
void __init early_fixmap_init(void)
|
|
{
|
|
pgd_t *pgdp, pgd;
|
|
pud_t *pudp;
|
|
pmd_t *pmdp;
|
|
unsigned long addr = FIXADDR_START;
|
|
|
|
pgdp = pgd_offset_k(addr);
|
|
pgd = READ_ONCE(*pgdp);
|
|
if (CONFIG_PGTABLE_LEVELS > 3 &&
|
|
!(pgd_none(pgd) || pgd_page_paddr(pgd) == __pa_symbol(bm_pud))) {
|
|
/*
|
|
* We only end up here if the kernel mapping and the fixmap
|
|
* share the top level pgd entry, which should only happen on
|
|
* 16k/4 levels configurations.
|
|
*/
|
|
BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
|
|
pudp = pud_offset_kimg(pgdp, addr);
|
|
} else {
|
|
if (pgd_none(pgd))
|
|
__pgd_populate(pgdp, __pa_symbol(bm_pud), PUD_TYPE_TABLE);
|
|
pudp = fixmap_pud(addr);
|
|
}
|
|
if (pud_none(READ_ONCE(*pudp)))
|
|
__pud_populate(pudp, __pa_symbol(bm_pmd), PMD_TYPE_TABLE);
|
|
pmdp = fixmap_pmd(addr);
|
|
__pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE);
|
|
|
|
/*
|
|
* The boot-ioremap range spans multiple pmds, for which
|
|
* we are not prepared:
|
|
*/
|
|
BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
|
|
!= (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
|
|
|
|
if ((pmdp != fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)))
|
|
|| pmdp != fixmap_pmd(fix_to_virt(FIX_BTMAP_END))) {
|
|
WARN_ON(1);
|
|
pr_warn("pmdp %p != %p, %p\n",
|
|
pmdp, fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)),
|
|
fixmap_pmd(fix_to_virt(FIX_BTMAP_END)));
|
|
pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
|
|
fix_to_virt(FIX_BTMAP_BEGIN));
|
|
pr_warn("fix_to_virt(FIX_BTMAP_END): %08lx\n",
|
|
fix_to_virt(FIX_BTMAP_END));
|
|
|
|
pr_warn("FIX_BTMAP_END: %d\n", FIX_BTMAP_END);
|
|
pr_warn("FIX_BTMAP_BEGIN: %d\n", FIX_BTMAP_BEGIN);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we
|
|
* ever need to use IPIs for TLB broadcasting, then we're in trouble here.
|
|
*/
|
|
void __set_fixmap(enum fixed_addresses idx,
|
|
phys_addr_t phys, pgprot_t flags)
|
|
{
|
|
unsigned long addr = __fix_to_virt(idx);
|
|
pte_t *ptep;
|
|
|
|
BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses);
|
|
|
|
ptep = fixmap_pte(addr);
|
|
|
|
if (pgprot_val(flags)) {
|
|
set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags));
|
|
} else {
|
|
pte_clear(&init_mm, addr, ptep);
|
|
flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
|
|
{
|
|
const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
|
|
int offset;
|
|
void *dt_virt;
|
|
|
|
/*
|
|
* Check whether the physical FDT address is set and meets the minimum
|
|
* alignment requirement. Since we are relying on MIN_FDT_ALIGN to be
|
|
* at least 8 bytes so that we can always access the magic and size
|
|
* fields of the FDT header after mapping the first chunk, double check
|
|
* here if that is indeed the case.
|
|
*/
|
|
BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
|
|
if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
|
|
return NULL;
|
|
|
|
/*
|
|
* Make sure that the FDT region can be mapped without the need to
|
|
* allocate additional translation table pages, so that it is safe
|
|
* to call create_mapping_noalloc() this early.
|
|
*
|
|
* On 64k pages, the FDT will be mapped using PTEs, so we need to
|
|
* be in the same PMD as the rest of the fixmap.
|
|
* On 4k pages, we'll use section mappings for the FDT so we only
|
|
* have to be in the same PUD.
|
|
*/
|
|
BUILD_BUG_ON(dt_virt_base % SZ_2M);
|
|
|
|
BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> SWAPPER_TABLE_SHIFT !=
|
|
__fix_to_virt(FIX_BTMAP_BEGIN) >> SWAPPER_TABLE_SHIFT);
|
|
|
|
offset = dt_phys % SWAPPER_BLOCK_SIZE;
|
|
dt_virt = (void *)dt_virt_base + offset;
|
|
|
|
/* map the first chunk so we can read the size from the header */
|
|
create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
|
|
dt_virt_base, SWAPPER_BLOCK_SIZE, prot);
|
|
|
|
if (fdt_magic(dt_virt) != FDT_MAGIC)
|
|
return NULL;
|
|
|
|
*size = fdt_totalsize(dt_virt);
|
|
if (*size > MAX_FDT_SIZE)
|
|
return NULL;
|
|
|
|
if (offset + *size > SWAPPER_BLOCK_SIZE)
|
|
create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base,
|
|
round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot);
|
|
|
|
return dt_virt;
|
|
}
|
|
|
|
int __init arch_ioremap_pud_supported(void)
|
|
{
|
|
/*
|
|
* Only 4k granule supports level 1 block mappings.
|
|
* SW table walks can't handle removal of intermediate entries.
|
|
*/
|
|
return IS_ENABLED(CONFIG_ARM64_4K_PAGES) &&
|
|
!IS_ENABLED(CONFIG_ARM64_PTDUMP_DEBUGFS);
|
|
}
|
|
|
|
int __init arch_ioremap_pmd_supported(void)
|
|
{
|
|
/* See arch_ioremap_pud_supported() */
|
|
return !IS_ENABLED(CONFIG_ARM64_PTDUMP_DEBUGFS);
|
|
}
|
|
|
|
int pud_set_huge(pud_t *pudp, phys_addr_t phys, pgprot_t prot)
|
|
{
|
|
pgprot_t sect_prot = __pgprot(PUD_TYPE_SECT |
|
|
pgprot_val(mk_sect_prot(prot)));
|
|
pud_t new_pud = pfn_pud(__phys_to_pfn(phys), sect_prot);
|
|
|
|
/* Only allow permission changes for now */
|
|
if (!pgattr_change_is_safe(READ_ONCE(pud_val(*pudp)),
|
|
pud_val(new_pud)))
|
|
return 0;
|
|
|
|
BUG_ON(phys & ~PUD_MASK);
|
|
set_pud(pudp, new_pud);
|
|
return 1;
|
|
}
|
|
|
|
int pmd_set_huge(pmd_t *pmdp, phys_addr_t phys, pgprot_t prot)
|
|
{
|
|
pgprot_t sect_prot = __pgprot(PMD_TYPE_SECT |
|
|
pgprot_val(mk_sect_prot(prot)));
|
|
pmd_t new_pmd = pfn_pmd(__phys_to_pfn(phys), sect_prot);
|
|
|
|
/* Only allow permission changes for now */
|
|
if (!pgattr_change_is_safe(READ_ONCE(pmd_val(*pmdp)),
|
|
pmd_val(new_pmd)))
|
|
return 0;
|
|
|
|
BUG_ON(phys & ~PMD_MASK);
|
|
set_pmd(pmdp, new_pmd);
|
|
return 1;
|
|
}
|
|
|
|
int pud_clear_huge(pud_t *pudp)
|
|
{
|
|
if (!pud_sect(READ_ONCE(*pudp)))
|
|
return 0;
|
|
pud_clear(pudp);
|
|
return 1;
|
|
}
|
|
|
|
int pmd_clear_huge(pmd_t *pmdp)
|
|
{
|
|
if (!pmd_sect(READ_ONCE(*pmdp)))
|
|
return 0;
|
|
pmd_clear(pmdp);
|
|
return 1;
|
|
}
|
|
|
|
int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
|
|
{
|
|
pte_t *table;
|
|
pmd_t pmd;
|
|
|
|
pmd = READ_ONCE(*pmdp);
|
|
|
|
if (!pmd_present(pmd))
|
|
return 1;
|
|
if (!pmd_table(pmd)) {
|
|
VM_WARN_ON(!pmd_table(pmd));
|
|
return 1;
|
|
}
|
|
|
|
table = pte_offset_kernel(pmdp, addr);
|
|
pmd_clear(pmdp);
|
|
__flush_tlb_kernel_pgtable(addr);
|
|
pte_free_kernel(NULL, table);
|
|
return 1;
|
|
}
|
|
|
|
int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
|
|
{
|
|
pmd_t *table;
|
|
pmd_t *pmdp;
|
|
pud_t pud;
|
|
unsigned long next, end;
|
|
|
|
pud = READ_ONCE(*pudp);
|
|
|
|
if (!pud_present(pud))
|
|
return 1;
|
|
if (!pud_table(pud)) {
|
|
VM_WARN_ON(!pud_table(pud));
|
|
return 1;
|
|
}
|
|
|
|
table = pmd_offset(pudp, addr);
|
|
pmdp = table;
|
|
next = addr;
|
|
end = addr + PUD_SIZE;
|
|
do {
|
|
pmd_free_pte_page(pmdp, next);
|
|
} while (pmdp++, next += PMD_SIZE, next != end);
|
|
|
|
pud_clear(pudp);
|
|
__flush_tlb_kernel_pgtable(addr);
|
|
pmd_free(NULL, table);
|
|
return 1;
|
|
}
|