mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
				synced 2025-11-04 07:44:51 +10:00 
			
		
		
		
	Merge branches 'for-next/52-bit-kva', 'for-next/cpu-topology', 'for-next/error-injection', 'for-next/perf', 'for-next/psci-cpuidle', 'for-next/rng', 'for-next/smpboot', 'for-next/tbi' and 'for-next/tlbi' into for-next/core
* for-next/52-bit-kva: (25 commits) Support for 52-bit virtual addressing in kernel space * for-next/cpu-topology: (9 commits) Move CPU topology parsing into core code and add support for ACPI 6.3 * for-next/error-injection: (2 commits) Support for function error injection via kprobes * for-next/perf: (8 commits) Support for i.MX8 DDR PMU and proper SMMUv3 group validation * for-next/psci-cpuidle: (7 commits) Move PSCI idle code into a new CPUidle driver * for-next/rng: (4 commits) Support for 'rng-seed' property being passed in the devicetree * for-next/smpboot: (3 commits) Reduce fragility of secondary CPU bringup in debug configurations * for-next/tbi: (10 commits) Introduce new syscall ABI with relaxed requirements for pointer tags * for-next/tlbi: (6 commits) Handle spurious page faults arising from kernel space
This commit is contained in:
		
							parent
							
								
									f32c7a8e45
								
							
								
									b333b0ba23
								
							
								
									d06fa5a118
								
							
								
									42d038c4fb
								
							
								
									3724e186fe
								
							
								
									d55c5f28af
								
							
								
									dd753d961c
								
							
								
									ebef746543
								
							
								
									92af2b6961
								
							
								
									5c062ef415
								
							
						
					
					
						commit
						ac12cf85d6
					
				
							
								
								
									
										52
									
								
								Documentation/admin-guide/perf/imx-ddr.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Documentation/admin-guide/perf/imx-ddr.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
=====================================================
 | 
			
		||||
Freescale i.MX8 DDR Performance Monitoring Unit (PMU)
 | 
			
		||||
=====================================================
 | 
			
		||||
 | 
			
		||||
There are no performance counters inside the DRAM controller, so performance
 | 
			
		||||
signals are brought out to the edge of the controller where a set of 4 x 32 bit
 | 
			
		||||
counters is implemented. This is controlled by the CSV modes programed in counter
 | 
			
		||||
control register which causes a large number of PERF signals to be generated.
 | 
			
		||||
 | 
			
		||||
Selection of the value for each counter is done via the config registers. There
 | 
			
		||||
is one register for each counter. Counter 0 is special in that it always counts
 | 
			
		||||
“time” and when expired causes a lock on itself and the other counters and an
 | 
			
		||||
interrupt is raised. If any other counter overflows, it continues counting, and
 | 
			
		||||
no interrupt is raised.
 | 
			
		||||
 | 
			
		||||
The "format" directory describes format of the config (event ID) and config1
 | 
			
		||||
(AXI filtering) fields of the perf_event_attr structure, see /sys/bus/event_source/
 | 
			
		||||
devices/imx8_ddr0/format/. The "events" directory describes the events types
 | 
			
		||||
hardware supported that can be used with perf tool, see /sys/bus/event_source/
 | 
			
		||||
devices/imx8_ddr0/events/.
 | 
			
		||||
  e.g.::
 | 
			
		||||
        perf stat -a -e imx8_ddr0/cycles/ cmd
 | 
			
		||||
        perf stat -a -e imx8_ddr0/read/,imx8_ddr0/write/ cmd
 | 
			
		||||
 | 
			
		||||
AXI filtering is only used by CSV modes 0x41 (axid-read) and 0x42 (axid-write)
 | 
			
		||||
to count reading or writing matches filter setting. Filter setting is various
 | 
			
		||||
from different DRAM controller implementations, which is distinguished by quirks
 | 
			
		||||
in the driver.
 | 
			
		||||
 | 
			
		||||
* With DDR_CAP_AXI_ID_FILTER quirk.
 | 
			
		||||
  Filter is defined with two configuration parts:
 | 
			
		||||
  --AXI_ID defines AxID matching value.
 | 
			
		||||
  --AXI_MASKING defines which bits of AxID are meaningful for the matching.
 | 
			
		||||
        0:corresponding bit is masked.
 | 
			
		||||
        1: corresponding bit is not masked, i.e. used to do the matching.
 | 
			
		||||
 | 
			
		||||
  AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance counter.
 | 
			
		||||
  When non-masked bits are matching corresponding AXI_ID bits then counter is
 | 
			
		||||
  incremented. Perf counter is incremented if
 | 
			
		||||
          AxID && AXI_MASKING == AXI_ID && AXI_MASKING
 | 
			
		||||
 | 
			
		||||
  This filter doesn't support filter different AXI ID for axid-read and axid-write
 | 
			
		||||
  event at the same time as this filter is shared between counters.
 | 
			
		||||
  e.g.::
 | 
			
		||||
        perf stat -a -e imx8_ddr0/axid-read,axi_mask=0xMMMM,axi_id=0xDDDD/ cmd
 | 
			
		||||
        perf stat -a -e imx8_ddr0/axid-write,axi_mask=0xMMMM,axi_id=0xDDDD/ cmd
 | 
			
		||||
 | 
			
		||||
  NOTE: axi_mask is inverted in userspace(i.e. set bits are bits to mask), and
 | 
			
		||||
  it will be reverted in driver automatically. so that the user can just specify
 | 
			
		||||
  axi_id to monitor a specific id, rather than having to specify axi_mask.
 | 
			
		||||
  e.g.::
 | 
			
		||||
        perf stat -a -e imx8_ddr0/axid-read,axi_id=0x12/ cmd, which will monitor ARID=0x12
 | 
			
		||||
@ -16,6 +16,7 @@ ARM64 Architecture
 | 
			
		||||
    pointer-authentication
 | 
			
		||||
    silicon-errata
 | 
			
		||||
    sve
 | 
			
		||||
    tagged-address-abi
 | 
			
		||||
    tagged-pointers
 | 
			
		||||
 | 
			
		||||
.. only::  subproject and html
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								Documentation/arm64/kasan-offsets.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Documentation/arm64/kasan-offsets.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
# Print out the KASAN_SHADOW_OFFSETS required to place the KASAN SHADOW
 | 
			
		||||
# start address at the mid-point of the kernel VA space
 | 
			
		||||
 | 
			
		||||
print_kasan_offset () {
 | 
			
		||||
	printf "%02d\t" $1
 | 
			
		||||
	printf "0x%08x00000000\n" $(( (0xffffffff & (-1 << ($1 - 1 - 32))) \
 | 
			
		||||
			+ (1 << ($1 - 32 - $2)) \
 | 
			
		||||
			- (1 << (64 - 32 - $2)) ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
echo KASAN_SHADOW_SCALE_SHIFT = 3
 | 
			
		||||
printf "VABITS\tKASAN_SHADOW_OFFSET\n"
 | 
			
		||||
print_kasan_offset 48 3
 | 
			
		||||
print_kasan_offset 47 3
 | 
			
		||||
print_kasan_offset 42 3
 | 
			
		||||
print_kasan_offset 39 3
 | 
			
		||||
print_kasan_offset 36 3
 | 
			
		||||
echo
 | 
			
		||||
echo KASAN_SHADOW_SCALE_SHIFT = 4
 | 
			
		||||
printf "VABITS\tKASAN_SHADOW_OFFSET\n"
 | 
			
		||||
print_kasan_offset 48 4
 | 
			
		||||
print_kasan_offset 47 4
 | 
			
		||||
print_kasan_offset 42 4
 | 
			
		||||
print_kasan_offset 39 4
 | 
			
		||||
print_kasan_offset 36 4
 | 
			
		||||
@ -14,6 +14,10 @@ with the 4KB page configuration, allowing 39-bit (512GB) or 48-bit
 | 
			
		||||
64KB pages, only 2 levels of translation tables, allowing 42-bit (4TB)
 | 
			
		||||
virtual address, are used but the memory layout is the same.
 | 
			
		||||
 | 
			
		||||
ARMv8.2 adds optional support for Large Virtual Address space. This is
 | 
			
		||||
only available when running with a 64KB page size and expands the
 | 
			
		||||
number of descriptors in the first level of translation.
 | 
			
		||||
 | 
			
		||||
User addresses have bits 63:48 set to 0 while the kernel addresses have
 | 
			
		||||
the same bits set to 1. TTBRx selection is given by bit 63 of the
 | 
			
		||||
virtual address. The swapper_pg_dir contains only kernel (global)
 | 
			
		||||
@ -22,40 +26,43 @@ The swapper_pg_dir address is written to TTBR1 and never written to
 | 
			
		||||
TTBR0.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AArch64 Linux memory layout with 4KB pages + 3 levels::
 | 
			
		||||
 | 
			
		||||
  Start			End			Size		Use
 | 
			
		||||
  -----------------------------------------------------------------------
 | 
			
		||||
  0000000000000000	0000007fffffffff	 512GB		user
 | 
			
		||||
  ffffff8000000000	ffffffffffffffff	 512GB		kernel
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AArch64 Linux memory layout with 4KB pages + 4 levels::
 | 
			
		||||
AArch64 Linux memory layout with 4KB pages + 4 levels (48-bit)::
 | 
			
		||||
 | 
			
		||||
  Start			End			Size		Use
 | 
			
		||||
  -----------------------------------------------------------------------
 | 
			
		||||
  0000000000000000	0000ffffffffffff	 256TB		user
 | 
			
		||||
  ffff000000000000	ffffffffffffffff	 256TB		kernel
 | 
			
		||||
  ffff000000000000	ffff7fffffffffff	 128TB		kernel logical memory map
 | 
			
		||||
  ffff800000000000	ffff9fffffffffff	  32TB		kasan shadow region
 | 
			
		||||
  ffffa00000000000	ffffa00007ffffff	 128MB		bpf jit region
 | 
			
		||||
  ffffa00008000000	ffffa0000fffffff	 128MB		modules
 | 
			
		||||
  ffffa00010000000	fffffdffbffeffff	 ~93TB		vmalloc
 | 
			
		||||
  fffffdffbfff0000	fffffdfffe5f8fff	~998MB		[guard region]
 | 
			
		||||
  fffffdfffe5f9000	fffffdfffe9fffff	4124KB		fixed mappings
 | 
			
		||||
  fffffdfffea00000	fffffdfffebfffff	   2MB		[guard region]
 | 
			
		||||
  fffffdfffec00000	fffffdffffbfffff	  16MB		PCI I/O space
 | 
			
		||||
  fffffdffffc00000	fffffdffffdfffff	   2MB		[guard region]
 | 
			
		||||
  fffffdffffe00000	ffffffffffdfffff	   2TB		vmemmap
 | 
			
		||||
  ffffffffffe00000	ffffffffffffffff	   2MB		[guard region]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AArch64 Linux memory layout with 64KB pages + 2 levels::
 | 
			
		||||
AArch64 Linux memory layout with 64KB pages + 3 levels (52-bit with HW support)::
 | 
			
		||||
 | 
			
		||||
  Start			End			Size		Use
 | 
			
		||||
  -----------------------------------------------------------------------
 | 
			
		||||
  0000000000000000	000003ffffffffff	   4TB		user
 | 
			
		||||
  fffffc0000000000	ffffffffffffffff	   4TB		kernel
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AArch64 Linux memory layout with 64KB pages + 3 levels::
 | 
			
		||||
 | 
			
		||||
  Start			End			Size		Use
 | 
			
		||||
  -----------------------------------------------------------------------
 | 
			
		||||
  0000000000000000	0000ffffffffffff	 256TB		user
 | 
			
		||||
  ffff000000000000	ffffffffffffffff	 256TB		kernel
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
For details of the virtual kernel memory layout please see the kernel
 | 
			
		||||
booting log.
 | 
			
		||||
  0000000000000000	000fffffffffffff	   4PB		user
 | 
			
		||||
  fff0000000000000	fff7ffffffffffff	   2PB		kernel logical memory map
 | 
			
		||||
  fff8000000000000	fffd9fffffffffff	1440TB		[gap]
 | 
			
		||||
  fffda00000000000	ffff9fffffffffff	 512TB		kasan shadow region
 | 
			
		||||
  ffffa00000000000	ffffa00007ffffff	 128MB		bpf jit region
 | 
			
		||||
  ffffa00008000000	ffffa0000fffffff	 128MB		modules
 | 
			
		||||
  ffffa00010000000	fffff81ffffeffff	 ~88TB		vmalloc
 | 
			
		||||
  fffff81fffff0000	fffffc1ffe58ffff	  ~3TB		[guard region]
 | 
			
		||||
  fffffc1ffe590000	fffffc1ffe9fffff	4544KB		fixed mappings
 | 
			
		||||
  fffffc1ffea00000	fffffc1ffebfffff	   2MB		[guard region]
 | 
			
		||||
  fffffc1ffec00000	fffffc1fffbfffff	  16MB		PCI I/O space
 | 
			
		||||
  fffffc1fffc00000	fffffc1fffdfffff	   2MB		[guard region]
 | 
			
		||||
  fffffc1fffe00000	ffffffffffdfffff	3968GB		vmemmap
 | 
			
		||||
  ffffffffffe00000	ffffffffffffffff	   2MB		[guard region]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Translation table lookup with 4KB pages::
 | 
			
		||||
@ -83,7 +90,8 @@ Translation table lookup with 64KB pages::
 | 
			
		||||
   |                 |    |               |            [15:0]  in-page offset
 | 
			
		||||
   |                 |    |               +----------> [28:16] L3 index
 | 
			
		||||
   |                 |    +--------------------------> [41:29] L2 index
 | 
			
		||||
   |                 +-------------------------------> [47:42] L1 index
 | 
			
		||||
   |                 +-------------------------------> [47:42] L1 index (48-bit)
 | 
			
		||||
   |                                                   [51:42] L1 index (52-bit)
 | 
			
		||||
   +-------------------------------------------------> [63] TTBR0/1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -96,3 +104,62 @@ ARM64_HARDEN_EL2_VECTORS is selected for particular CPUs.
 | 
			
		||||
 | 
			
		||||
When using KVM with the Virtualization Host Extensions, no additional
 | 
			
		||||
mappings are created, since the host kernel runs directly in EL2.
 | 
			
		||||
 | 
			
		||||
52-bit VA support in the kernel
 | 
			
		||||
-------------------------------
 | 
			
		||||
If the ARMv8.2-LVA optional feature is present, and we are running
 | 
			
		||||
with a 64KB page size; then it is possible to use 52-bits of address
 | 
			
		||||
space for both userspace and kernel addresses. However, any kernel
 | 
			
		||||
binary that supports 52-bit must also be able to fall back to 48-bit
 | 
			
		||||
at early boot time if the hardware feature is not present.
 | 
			
		||||
 | 
			
		||||
This fallback mechanism necessitates the kernel .text to be in the
 | 
			
		||||
higher addresses such that they are invariant to 48/52-bit VAs. Due
 | 
			
		||||
to the kasan shadow being a fraction of the entire kernel VA space,
 | 
			
		||||
the end of the kasan shadow must also be in the higher half of the
 | 
			
		||||
kernel VA space for both 48/52-bit. (Switching from 48-bit to 52-bit,
 | 
			
		||||
the end of the kasan shadow is invariant and dependent on ~0UL,
 | 
			
		||||
whilst the start address will "grow" towards the lower addresses).
 | 
			
		||||
 | 
			
		||||
In order to optimise phys_to_virt and virt_to_phys, the PAGE_OFFSET
 | 
			
		||||
is kept constant at 0xFFF0000000000000 (corresponding to 52-bit),
 | 
			
		||||
this obviates the need for an extra variable read. The physvirt
 | 
			
		||||
offset and vmemmap offsets are computed at early boot to enable
 | 
			
		||||
this logic.
 | 
			
		||||
 | 
			
		||||
As a single binary will need to support both 48-bit and 52-bit VA
 | 
			
		||||
spaces, the VMEMMAP must be sized large enough for 52-bit VAs and
 | 
			
		||||
also must be sized large enought to accommodate a fixed PAGE_OFFSET.
 | 
			
		||||
 | 
			
		||||
Most code in the kernel should not need to consider the VA_BITS, for
 | 
			
		||||
code that does need to know the VA size the variables are
 | 
			
		||||
defined as follows:
 | 
			
		||||
 | 
			
		||||
VA_BITS		constant	the *maximum* VA space size
 | 
			
		||||
 | 
			
		||||
VA_BITS_MIN	constant	the *minimum* VA space size
 | 
			
		||||
 | 
			
		||||
vabits_actual	variable	the *actual* VA space size
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Maximum and minimum sizes can be useful to ensure that buffers are
 | 
			
		||||
sized large enough or that addresses are positioned close enough for
 | 
			
		||||
the "worst" case.
 | 
			
		||||
 | 
			
		||||
52-bit userspace VAs
 | 
			
		||||
--------------------
 | 
			
		||||
To maintain compatibility with software that relies on the ARMv8.0
 | 
			
		||||
VA space maximum size of 48-bits, the kernel will, by default,
 | 
			
		||||
return virtual addresses to userspace from a 48-bit range.
 | 
			
		||||
 | 
			
		||||
Software can "opt-in" to receiving VAs from a 52-bit space by
 | 
			
		||||
specifying an mmap hint parameter that is larger than 48-bit.
 | 
			
		||||
For example:
 | 
			
		||||
    maybe_high_address = mmap(~0UL, size, prot, flags,...);
 | 
			
		||||
 | 
			
		||||
It is also possible to build a debug kernel that returns addresses
 | 
			
		||||
from a 52-bit space by enabling the following kernel config options:
 | 
			
		||||
   CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
 | 
			
		||||
 | 
			
		||||
Note that this option is only intended for debugging applications
 | 
			
		||||
and should not be used in production.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										156
									
								
								Documentation/arm64/tagged-address-abi.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								Documentation/arm64/tagged-address-abi.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,156 @@
 | 
			
		||||
==========================
 | 
			
		||||
AArch64 TAGGED ADDRESS ABI
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
Authors: Vincenzo Frascino <vincenzo.frascino@arm.com>
 | 
			
		||||
         Catalin Marinas <catalin.marinas@arm.com>
 | 
			
		||||
 | 
			
		||||
Date: 21 August 2019
 | 
			
		||||
 | 
			
		||||
This document describes the usage and semantics of the Tagged Address
 | 
			
		||||
ABI on AArch64 Linux.
 | 
			
		||||
 | 
			
		||||
1. Introduction
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
On AArch64 the ``TCR_EL1.TBI0`` bit is set by default, allowing
 | 
			
		||||
userspace (EL0) to perform memory accesses through 64-bit pointers with
 | 
			
		||||
a non-zero top byte. This document describes the relaxation of the
 | 
			
		||||
syscall ABI that allows userspace to pass certain tagged pointers to
 | 
			
		||||
kernel syscalls.
 | 
			
		||||
 | 
			
		||||
2. AArch64 Tagged Address ABI
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
From the kernel syscall interface perspective and for the purposes of
 | 
			
		||||
this document, a "valid tagged pointer" is a pointer with a potentially
 | 
			
		||||
non-zero top-byte that references an address in the user process address
 | 
			
		||||
space obtained in one of the following ways:
 | 
			
		||||
 | 
			
		||||
- ``mmap()`` syscall where either:
 | 
			
		||||
 | 
			
		||||
  - flags have the ``MAP_ANONYMOUS`` bit set or
 | 
			
		||||
  - the file descriptor refers to a regular file (including those
 | 
			
		||||
    returned by ``memfd_create()``) or ``/dev/zero``
 | 
			
		||||
 | 
			
		||||
- ``brk()`` syscall (i.e. the heap area between the initial location of
 | 
			
		||||
  the program break at process creation and its current location).
 | 
			
		||||
 | 
			
		||||
- any memory mapped by the kernel in the address space of the process
 | 
			
		||||
  during creation and with the same restrictions as for ``mmap()`` above
 | 
			
		||||
  (e.g. data, bss, stack).
 | 
			
		||||
 | 
			
		||||
The AArch64 Tagged Address ABI has two stages of relaxation depending
 | 
			
		||||
how the user addresses are used by the kernel:
 | 
			
		||||
 | 
			
		||||
1. User addresses not accessed by the kernel but used for address space
 | 
			
		||||
   management (e.g. ``mmap()``, ``mprotect()``, ``madvise()``). The use
 | 
			
		||||
   of valid tagged pointers in this context is always allowed.
 | 
			
		||||
 | 
			
		||||
2. User addresses accessed by the kernel (e.g. ``write()``). This ABI
 | 
			
		||||
   relaxation is disabled by default and the application thread needs to
 | 
			
		||||
   explicitly enable it via ``prctl()`` as follows:
 | 
			
		||||
 | 
			
		||||
   - ``PR_SET_TAGGED_ADDR_CTRL``: enable or disable the AArch64 Tagged
 | 
			
		||||
     Address ABI for the calling thread.
 | 
			
		||||
 | 
			
		||||
     The ``(unsigned int) arg2`` argument is a bit mask describing the
 | 
			
		||||
     control mode used:
 | 
			
		||||
 | 
			
		||||
     - ``PR_TAGGED_ADDR_ENABLE``: enable AArch64 Tagged Address ABI.
 | 
			
		||||
       Default status is disabled.
 | 
			
		||||
 | 
			
		||||
     Arguments ``arg3``, ``arg4``, and ``arg5`` must be 0.
 | 
			
		||||
 | 
			
		||||
   - ``PR_GET_TAGGED_ADDR_CTRL``: get the status of the AArch64 Tagged
 | 
			
		||||
     Address ABI for the calling thread.
 | 
			
		||||
 | 
			
		||||
     Arguments ``arg2``, ``arg3``, ``arg4``, and ``arg5`` must be 0.
 | 
			
		||||
 | 
			
		||||
   The ABI properties described above are thread-scoped, inherited on
 | 
			
		||||
   clone() and fork() and cleared on exec().
 | 
			
		||||
 | 
			
		||||
   Calling ``prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0)``
 | 
			
		||||
   returns ``-EINVAL`` if the AArch64 Tagged Address ABI is globally
 | 
			
		||||
   disabled by ``sysctl abi.tagged_addr_disabled=1``. The default
 | 
			
		||||
   ``sysctl abi.tagged_addr_disabled`` configuration is 0.
 | 
			
		||||
 | 
			
		||||
When the AArch64 Tagged Address ABI is enabled for a thread, the
 | 
			
		||||
following behaviours are guaranteed:
 | 
			
		||||
 | 
			
		||||
- All syscalls except the cases mentioned in section 3 can accept any
 | 
			
		||||
  valid tagged pointer.
 | 
			
		||||
 | 
			
		||||
- The syscall behaviour is undefined for invalid tagged pointers: it may
 | 
			
		||||
  result in an error code being returned, a (fatal) signal being raised,
 | 
			
		||||
  or other modes of failure.
 | 
			
		||||
 | 
			
		||||
- The syscall behaviour for a valid tagged pointer is the same as for
 | 
			
		||||
  the corresponding untagged pointer.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
A definition of the meaning of tagged pointers on AArch64 can be found
 | 
			
		||||
in Documentation/arm64/tagged-pointers.rst.
 | 
			
		||||
 | 
			
		||||
3. AArch64 Tagged Address ABI Exceptions
 | 
			
		||||
-----------------------------------------
 | 
			
		||||
 | 
			
		||||
The following system call parameters must be untagged regardless of the
 | 
			
		||||
ABI relaxation:
 | 
			
		||||
 | 
			
		||||
- ``prctl()`` other than pointers to user data either passed directly or
 | 
			
		||||
  indirectly as arguments to be accessed by the kernel.
 | 
			
		||||
 | 
			
		||||
- ``ioctl()`` other than pointers to user data either passed directly or
 | 
			
		||||
  indirectly as arguments to be accessed by the kernel.
 | 
			
		||||
 | 
			
		||||
- ``shmat()`` and ``shmdt()``.
 | 
			
		||||
 | 
			
		||||
Any attempt to use non-zero tagged pointers may result in an error code
 | 
			
		||||
being returned, a (fatal) signal being raised, or other modes of
 | 
			
		||||
failure.
 | 
			
		||||
 | 
			
		||||
4. Example of correct usage
 | 
			
		||||
---------------------------
 | 
			
		||||
.. code-block:: c
 | 
			
		||||
 | 
			
		||||
   #include <stdlib.h>
 | 
			
		||||
   #include <string.h>
 | 
			
		||||
   #include <unistd.h>
 | 
			
		||||
   #include <sys/mman.h>
 | 
			
		||||
   #include <sys/prctl.h>
 | 
			
		||||
   
 | 
			
		||||
   #define PR_SET_TAGGED_ADDR_CTRL	55
 | 
			
		||||
   #define PR_TAGGED_ADDR_ENABLE	(1UL << 0)
 | 
			
		||||
   
 | 
			
		||||
   #define TAG_SHIFT		56
 | 
			
		||||
   
 | 
			
		||||
   int main(void)
 | 
			
		||||
   {
 | 
			
		||||
   	int tbi_enabled = 0;
 | 
			
		||||
   	unsigned long tag = 0;
 | 
			
		||||
   	char *ptr;
 | 
			
		||||
   
 | 
			
		||||
   	/* check/enable the tagged address ABI */
 | 
			
		||||
   	if (!prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0))
 | 
			
		||||
   		tbi_enabled = 1;
 | 
			
		||||
   
 | 
			
		||||
   	/* memory allocation */
 | 
			
		||||
   	ptr = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
 | 
			
		||||
   		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 | 
			
		||||
   	if (ptr == MAP_FAILED)
 | 
			
		||||
   		return 1;
 | 
			
		||||
   
 | 
			
		||||
   	/* set a non-zero tag if the ABI is available */
 | 
			
		||||
   	if (tbi_enabled)
 | 
			
		||||
   		tag = rand() & 0xff;
 | 
			
		||||
   	ptr = (char *)((unsigned long)ptr | (tag << TAG_SHIFT));
 | 
			
		||||
   
 | 
			
		||||
   	/* memory access to a tagged address */
 | 
			
		||||
   	strcpy(ptr, "tagged pointer\n");
 | 
			
		||||
   
 | 
			
		||||
   	/* syscall with a tagged pointer */
 | 
			
		||||
   	write(1, ptr, strlen(ptr));
 | 
			
		||||
   
 | 
			
		||||
   	return 0;
 | 
			
		||||
   }
 | 
			
		||||
@ -20,7 +20,9 @@ Passing tagged addresses to the kernel
 | 
			
		||||
--------------------------------------
 | 
			
		||||
 | 
			
		||||
All interpretation of userspace memory addresses by the kernel assumes
 | 
			
		||||
an address tag of 0x00.
 | 
			
		||||
an address tag of 0x00, unless the application enables the AArch64
 | 
			
		||||
Tagged Address ABI explicitly
 | 
			
		||||
(Documentation/arm64/tagged-address-abi.rst).
 | 
			
		||||
 | 
			
		||||
This includes, but is not limited to, addresses found in:
 | 
			
		||||
 | 
			
		||||
@ -33,13 +35,15 @@ This includes, but is not limited to, addresses found in:
 | 
			
		||||
 - the frame pointer (x29) and frame records, e.g. when interpreting
 | 
			
		||||
   them to generate a backtrace or call graph.
 | 
			
		||||
 | 
			
		||||
Using non-zero address tags in any of these locations may result in an
 | 
			
		||||
error code being returned, a (fatal) signal being raised, or other modes
 | 
			
		||||
of failure.
 | 
			
		||||
Using non-zero address tags in any of these locations when the
 | 
			
		||||
userspace application did not enable the AArch64 Tagged Address ABI may
 | 
			
		||||
result in an error code being returned, a (fatal) signal being raised,
 | 
			
		||||
or other modes of failure.
 | 
			
		||||
 | 
			
		||||
For these reasons, passing non-zero address tags to the kernel via
 | 
			
		||||
system calls is forbidden, and using a non-zero address tag for sp is
 | 
			
		||||
strongly discouraged.
 | 
			
		||||
For these reasons, when the AArch64 Tagged Address ABI is disabled,
 | 
			
		||||
passing non-zero address tags to the kernel via system calls is
 | 
			
		||||
forbidden, and using a non-zero address tag for sp is strongly
 | 
			
		||||
discouraged.
 | 
			
		||||
 | 
			
		||||
Programs maintaining a frame pointer and frame records that use non-zero
 | 
			
		||||
address tags may suffer impaired or inaccurate debug and profiling
 | 
			
		||||
@ -59,6 +63,9 @@ be preserved.
 | 
			
		||||
The architecture prevents the use of a tagged PC, so the upper byte will
 | 
			
		||||
be set to a sign-extension of bit 55 on exception return.
 | 
			
		||||
 | 
			
		||||
This behaviour is maintained when the AArch64 Tagged Address ABI is
 | 
			
		||||
enabled.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Other considerations
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,19 @@
 | 
			
		||||
===========================================
 | 
			
		||||
ARM topology binding description
 | 
			
		||||
CPU topology binding description
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
===========================================
 | 
			
		||||
1 - Introduction
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
In an ARM system, the hierarchy of CPUs is defined through three entities that
 | 
			
		||||
In a SMP system, the hierarchy of CPUs is defined through three entities that
 | 
			
		||||
are used to describe the layout of physical CPUs in the system:
 | 
			
		||||
 | 
			
		||||
- socket
 | 
			
		||||
- cluster
 | 
			
		||||
- core
 | 
			
		||||
- thread
 | 
			
		||||
 | 
			
		||||
The cpu nodes (bindings defined in [1]) represent the devices that
 | 
			
		||||
correspond to physical CPUs and are to be mapped to the hierarchy levels.
 | 
			
		||||
 | 
			
		||||
The bottom hierarchy level sits at core or thread level depending on whether
 | 
			
		||||
symmetric multi-threading (SMT) is supported or not.
 | 
			
		||||
 | 
			
		||||
@ -24,33 +22,31 @@ threads existing in the system and map to the hierarchy level "thread" above.
 | 
			
		||||
In systems where SMT is not supported "cpu" nodes represent all cores present
 | 
			
		||||
in the system and map to the hierarchy level "core" above.
 | 
			
		||||
 | 
			
		||||
ARM topology bindings allow one to associate cpu nodes with hierarchical groups
 | 
			
		||||
CPU topology bindings allow one to associate cpu nodes with hierarchical groups
 | 
			
		||||
corresponding to the system hierarchy; syntactically they are defined as device
 | 
			
		||||
tree nodes.
 | 
			
		||||
 | 
			
		||||
The remainder of this document provides the topology bindings for ARM, based
 | 
			
		||||
on the Devicetree Specification, available from:
 | 
			
		||||
Currently, only ARM/RISC-V intend to use this cpu topology binding but it may be
 | 
			
		||||
used for any other architecture as well.
 | 
			
		||||
 | 
			
		||||
https://www.devicetree.org/specifications/
 | 
			
		||||
The cpu nodes, as per bindings defined in [4], represent the devices that
 | 
			
		||||
correspond to physical CPUs and are to be mapped to the hierarchy levels.
 | 
			
		||||
 | 
			
		||||
If not stated otherwise, whenever a reference to a cpu node phandle is made its
 | 
			
		||||
value must point to a cpu node compliant with the cpu node bindings as
 | 
			
		||||
documented in [1].
 | 
			
		||||
A topology description containing phandles to cpu nodes that are not compliant
 | 
			
		||||
with bindings standardized in [1] is therefore considered invalid.
 | 
			
		||||
with bindings standardized in [4] is therefore considered invalid.
 | 
			
		||||
 | 
			
		||||
===========================================
 | 
			
		||||
2 - cpu-map node
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
The ARM CPU topology is defined within the cpu-map node, which is a direct
 | 
			
		||||
The ARM/RISC-V CPU topology is defined within the cpu-map node, which is a direct
 | 
			
		||||
child of the cpus node and provides a container where the actual topology
 | 
			
		||||
nodes are listed.
 | 
			
		||||
 | 
			
		||||
- cpu-map node
 | 
			
		||||
 | 
			
		||||
	Usage: Optional - On ARM SMP systems provide CPUs topology to the OS.
 | 
			
		||||
			  ARM uniprocessor systems do not require a topology
 | 
			
		||||
	Usage: Optional - On SMP systems provide CPUs topology to the OS.
 | 
			
		||||
			  Uniprocessor systems do not require a topology
 | 
			
		||||
			  description and therefore should not define a
 | 
			
		||||
			  cpu-map node.
 | 
			
		||||
 | 
			
		||||
@ -63,21 +59,23 @@ nodes are listed.
 | 
			
		||||
 | 
			
		||||
	The cpu-map node's child nodes can be:
 | 
			
		||||
 | 
			
		||||
	- one or more cluster nodes
 | 
			
		||||
	- one or more cluster nodes or
 | 
			
		||||
	- one or more socket nodes in a multi-socket system
 | 
			
		||||
 | 
			
		||||
	Any other configuration is considered invalid.
 | 
			
		||||
 | 
			
		||||
The cpu-map node can only contain three types of child nodes:
 | 
			
		||||
The cpu-map node can only contain 4 types of child nodes:
 | 
			
		||||
 | 
			
		||||
- socket node
 | 
			
		||||
- cluster node
 | 
			
		||||
- core node
 | 
			
		||||
- thread node
 | 
			
		||||
 | 
			
		||||
whose bindings are described in paragraph 3.
 | 
			
		||||
 | 
			
		||||
The nodes describing the CPU topology (cluster/core/thread) can only
 | 
			
		||||
be defined within the cpu-map node and every core/thread in the system
 | 
			
		||||
must be defined within the topology.  Any other configuration is
 | 
			
		||||
The nodes describing the CPU topology (socket/cluster/core/thread) can
 | 
			
		||||
only be defined within the cpu-map node and every core/thread in the
 | 
			
		||||
system must be defined within the topology.  Any other configuration is
 | 
			
		||||
invalid and therefore must be ignored.
 | 
			
		||||
 | 
			
		||||
===========================================
 | 
			
		||||
@ -85,26 +83,44 @@ invalid and therefore must be ignored.
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
cpu-map child nodes must follow a naming convention where the node name
 | 
			
		||||
must be "clusterN", "coreN", "threadN" depending on the node type (ie
 | 
			
		||||
cluster/core/thread) (where N = {0, 1, ...} is the node number; nodes which
 | 
			
		||||
are siblings within a single common parent node must be given a unique and
 | 
			
		||||
must be "socketN", "clusterN", "coreN", "threadN" depending on the node type
 | 
			
		||||
(ie socket/cluster/core/thread) (where N = {0, 1, ...} is the node number; nodes
 | 
			
		||||
which are siblings within a single common parent node must be given a unique and
 | 
			
		||||
sequential N value, starting from 0).
 | 
			
		||||
cpu-map child nodes which do not share a common parent node can have the same
 | 
			
		||||
name (ie same number N as other cpu-map child nodes at different device tree
 | 
			
		||||
levels) since name uniqueness will be guaranteed by the device tree hierarchy.
 | 
			
		||||
 | 
			
		||||
===========================================
 | 
			
		||||
3 - cluster/core/thread node bindings
 | 
			
		||||
3 - socket/cluster/core/thread node bindings
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
Bindings for cluster/cpu/thread nodes are defined as follows:
 | 
			
		||||
Bindings for socket/cluster/cpu/thread nodes are defined as follows:
 | 
			
		||||
 | 
			
		||||
- socket node
 | 
			
		||||
 | 
			
		||||
	 Description: must be declared within a cpu-map node, one node
 | 
			
		||||
		      per physical socket in the system. A system can
 | 
			
		||||
		      contain single or multiple physical socket.
 | 
			
		||||
		      The association of sockets and NUMA nodes is beyond
 | 
			
		||||
		      the scope of this bindings, please refer [2] for
 | 
			
		||||
		      NUMA bindings.
 | 
			
		||||
 | 
			
		||||
	This node is optional for a single socket system.
 | 
			
		||||
 | 
			
		||||
	The socket node name must be "socketN" as described in 2.1 above.
 | 
			
		||||
	A socket node can not be a leaf node.
 | 
			
		||||
 | 
			
		||||
	A socket node's child nodes must be one or more cluster nodes.
 | 
			
		||||
 | 
			
		||||
	Any other configuration is considered invalid.
 | 
			
		||||
 | 
			
		||||
- cluster node
 | 
			
		||||
 | 
			
		||||
	 Description: must be declared within a cpu-map node, one node
 | 
			
		||||
		      per cluster. A system can contain several layers of
 | 
			
		||||
		      clustering and cluster nodes can be contained in parent
 | 
			
		||||
		      cluster nodes.
 | 
			
		||||
		      clustering within a single physical socket and cluster
 | 
			
		||||
		      nodes can be contained in parent cluster nodes.
 | 
			
		||||
 | 
			
		||||
	The cluster node name must be "clusterN" as described in 2.1 above.
 | 
			
		||||
	A cluster node can not be a leaf node.
 | 
			
		||||
@ -164,90 +180,93 @@ Bindings for cluster/cpu/thread nodes are defined as follows:
 | 
			
		||||
4 - Example dts
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
Example 1 (ARM 64-bit, 16-cpu system, two clusters of clusters):
 | 
			
		||||
Example 1 (ARM 64-bit, 16-cpu system, two clusters of clusters in a single
 | 
			
		||||
physical socket):
 | 
			
		||||
 | 
			
		||||
cpus {
 | 
			
		||||
	#size-cells = <0>;
 | 
			
		||||
	#address-cells = <2>;
 | 
			
		||||
 | 
			
		||||
	cpu-map {
 | 
			
		||||
		cluster0 {
 | 
			
		||||
		socket0 {
 | 
			
		||||
			cluster0 {
 | 
			
		||||
				core0 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU0>;
 | 
			
		||||
				cluster0 {
 | 
			
		||||
					core0 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU0>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU1>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU1>;
 | 
			
		||||
 | 
			
		||||
					core1 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU2>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU3>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				core1 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU2>;
 | 
			
		||||
				cluster1 {
 | 
			
		||||
					core0 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU4>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU5>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU3>;
 | 
			
		||||
 | 
			
		||||
					core1 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU6>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU7>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			cluster1 {
 | 
			
		||||
				core0 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU4>;
 | 
			
		||||
				cluster0 {
 | 
			
		||||
					core0 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU8>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU9>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU5>;
 | 
			
		||||
					core1 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU10>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU11>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				core1 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU6>;
 | 
			
		||||
				cluster1 {
 | 
			
		||||
					core0 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU12>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU13>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU7>;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		cluster1 {
 | 
			
		||||
			cluster0 {
 | 
			
		||||
				core0 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU8>;
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU9>;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
				core1 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU10>;
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU11>;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			cluster1 {
 | 
			
		||||
				core0 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU12>;
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU13>;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
				core1 {
 | 
			
		||||
					thread0 {
 | 
			
		||||
						cpu = <&CPU14>;
 | 
			
		||||
					};
 | 
			
		||||
					thread1 {
 | 
			
		||||
						cpu = <&CPU15>;
 | 
			
		||||
					core1 {
 | 
			
		||||
						thread0 {
 | 
			
		||||
							cpu = <&CPU14>;
 | 
			
		||||
						};
 | 
			
		||||
						thread1 {
 | 
			
		||||
							cpu = <&CPU15>;
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
@ -470,6 +489,65 @@ cpus {
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Example 3: HiFive Unleashed (RISC-V 64 bit, 4 core system)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	#address-cells = <2>;
 | 
			
		||||
	#size-cells = <2>;
 | 
			
		||||
	compatible = "sifive,fu540g", "sifive,fu500";
 | 
			
		||||
	model = "sifive,hifive-unleashed-a00";
 | 
			
		||||
 | 
			
		||||
	...
 | 
			
		||||
	cpus {
 | 
			
		||||
		#address-cells = <1>;
 | 
			
		||||
		#size-cells = <0>;
 | 
			
		||||
		cpu-map {
 | 
			
		||||
			socket0 {
 | 
			
		||||
				cluster0 {
 | 
			
		||||
					core0 {
 | 
			
		||||
						cpu = <&CPU1>;
 | 
			
		||||
					};
 | 
			
		||||
					core1 {
 | 
			
		||||
						cpu = <&CPU2>;
 | 
			
		||||
					};
 | 
			
		||||
					core2 {
 | 
			
		||||
						cpu0 = <&CPU2>;
 | 
			
		||||
					};
 | 
			
		||||
					core3 {
 | 
			
		||||
						cpu0 = <&CPU3>;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		CPU1: cpu@1 {
 | 
			
		||||
			device_type = "cpu";
 | 
			
		||||
			compatible = "sifive,rocket0", "riscv";
 | 
			
		||||
			reg = <0x1>;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		CPU2: cpu@2 {
 | 
			
		||||
			device_type = "cpu";
 | 
			
		||||
			compatible = "sifive,rocket0", "riscv";
 | 
			
		||||
			reg = <0x2>;
 | 
			
		||||
		}
 | 
			
		||||
		CPU3: cpu@3 {
 | 
			
		||||
			device_type = "cpu";
 | 
			
		||||
			compatible = "sifive,rocket0", "riscv";
 | 
			
		||||
			reg = <0x3>;
 | 
			
		||||
		}
 | 
			
		||||
		CPU4: cpu@4 {
 | 
			
		||||
			device_type = "cpu";
 | 
			
		||||
			compatible = "sifive,rocket0", "riscv";
 | 
			
		||||
			reg = <0x4>;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
===============================================================================
 | 
			
		||||
[1] ARM Linux kernel documentation
 | 
			
		||||
    Documentation/devicetree/bindings/arm/cpus.yaml
 | 
			
		||||
[2] Devicetree NUMA binding description
 | 
			
		||||
    Documentation/devicetree/bindings/numa.txt
 | 
			
		||||
[3] RISC-V Linux kernel documentation
 | 
			
		||||
    Documentation/devicetree/bindings/riscv/cpus.txt
 | 
			
		||||
[4] https://www.devicetree.org/specifications/
 | 
			
		||||
							
								
								
									
										16
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								MAINTAINERS
									
									
									
									
									
								
							@ -4290,6 +4290,14 @@ S:	Supported
 | 
			
		||||
F:	drivers/cpuidle/cpuidle-exynos.c
 | 
			
		||||
F:	arch/arm/mach-exynos/pm.c
 | 
			
		||||
 | 
			
		||||
CPUIDLE DRIVER - ARM PSCI
 | 
			
		||||
M:	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
 | 
			
		||||
M:	Sudeep Holla <sudeep.holla@arm.com>
 | 
			
		||||
L:	linux-pm@vger.kernel.org
 | 
			
		||||
L:	linux-arm-kernel@lists.infradead.org
 | 
			
		||||
S:	Supported
 | 
			
		||||
F:	drivers/cpuidle/cpuidle-psci.c
 | 
			
		||||
 | 
			
		||||
CPU IDLE TIME MANAGEMENT FRAMEWORK
 | 
			
		||||
M:	"Rafael J. Wysocki" <rjw@rjwysocki.net>
 | 
			
		||||
M:	Daniel Lezcano <daniel.lezcano@linaro.org>
 | 
			
		||||
@ -6439,6 +6447,7 @@ M:	Frank Li <Frank.li@nxp.com>
 | 
			
		||||
L:	linux-arm-kernel@lists.infradead.org
 | 
			
		||||
S:	Maintained
 | 
			
		||||
F:	drivers/perf/fsl_imx8_ddr_perf.c
 | 
			
		||||
F:	Documentation/admin-guide/perf/imx-ddr.rst
 | 
			
		||||
F:	Documentation/devicetree/bindings/perf/fsl-imx-ddr.txt
 | 
			
		||||
 | 
			
		||||
FREESCALE IMX LPI2C DRIVER
 | 
			
		||||
@ -6724,6 +6733,13 @@ W:	https://linuxtv.org
 | 
			
		||||
S:	Maintained
 | 
			
		||||
F:	drivers/media/radio/radio-gemtek*
 | 
			
		||||
 | 
			
		||||
GENERIC ARCHITECTURE TOPOLOGY
 | 
			
		||||
M:	Sudeep Holla <sudeep.holla@arm.com>
 | 
			
		||||
L:	linux-kernel@vger.kernel.org
 | 
			
		||||
S:	Maintained
 | 
			
		||||
F:	drivers/base/arch_topology.c
 | 
			
		||||
F:	include/linux/arch_topology.h
 | 
			
		||||
 | 
			
		||||
GENERIC GPIO I2C DRIVER
 | 
			
		||||
M:	Wolfram Sang <wsa+renesas@sang-engineering.com>
 | 
			
		||||
S:	Supported
 | 
			
		||||
 | 
			
		||||
@ -5,26 +5,6 @@
 | 
			
		||||
#ifdef CONFIG_ARM_CPU_TOPOLOGY
 | 
			
		||||
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
 | 
			
		||||
struct cputopo_arm {
 | 
			
		||||
	int thread_id;
 | 
			
		||||
	int core_id;
 | 
			
		||||
	int socket_id;
 | 
			
		||||
	cpumask_t thread_sibling;
 | 
			
		||||
	cpumask_t core_sibling;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct cputopo_arm cpu_topology[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
#define topology_physical_package_id(cpu)	(cpu_topology[cpu].socket_id)
 | 
			
		||||
#define topology_core_id(cpu)		(cpu_topology[cpu].core_id)
 | 
			
		||||
#define topology_core_cpumask(cpu)	(&cpu_topology[cpu].core_sibling)
 | 
			
		||||
#define topology_sibling_cpumask(cpu)	(&cpu_topology[cpu].thread_sibling)
 | 
			
		||||
 | 
			
		||||
void init_cpu_topology(void);
 | 
			
		||||
void store_cpu_topology(unsigned int cpuid);
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu);
 | 
			
		||||
 | 
			
		||||
#include <linux/arch_topology.h>
 | 
			
		||||
 | 
			
		||||
/* Replace task scheduler's default frequency-invariant accounting */
 | 
			
		||||
 | 
			
		||||
@ -177,17 +177,6 @@ static inline void parse_dt_topology(void) {}
 | 
			
		||||
static inline void update_cpu_capacity(unsigned int cpuid) {}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 /*
 | 
			
		||||
 * cpu topology table
 | 
			
		||||
 */
 | 
			
		||||
struct cputopo_arm cpu_topology[NR_CPUS];
 | 
			
		||||
EXPORT_SYMBOL_GPL(cpu_topology);
 | 
			
		||||
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return &cpu_topology[cpu].core_sibling;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The current assumption is that we can power gate each core independently.
 | 
			
		||||
 * This will be superseded by DT binding once available.
 | 
			
		||||
@ -197,32 +186,6 @@ const struct cpumask *cpu_corepower_mask(int cpu)
 | 
			
		||||
	return &cpu_topology[cpu].thread_sibling;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_siblings_masks(unsigned int cpuid)
 | 
			
		||||
{
 | 
			
		||||
	struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	/* update core and thread sibling masks */
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->socket_id != cpu_topo->socket_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
 | 
			
		||||
		if (cpu != cpuid)
 | 
			
		||||
			cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->core_id != cpu_topo->core_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
 | 
			
		||||
		if (cpu != cpuid)
 | 
			
		||||
			cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
 | 
			
		||||
	}
 | 
			
		||||
	smp_wmb();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * store_cpu_topology is called at boot when only one cpu is running
 | 
			
		||||
 * and with the mutex cpu_hotplug.lock locked, when several cpus have booted,
 | 
			
		||||
@ -230,7 +193,7 @@ static void update_siblings_masks(unsigned int cpuid)
 | 
			
		||||
 */
 | 
			
		||||
void store_cpu_topology(unsigned int cpuid)
 | 
			
		||||
{
 | 
			
		||||
	struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
	struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
	unsigned int mpidr;
 | 
			
		||||
 | 
			
		||||
	/* If the cpu topology has been already set, just return */
 | 
			
		||||
@ -250,12 +213,12 @@ void store_cpu_topology(unsigned int cpuid)
 | 
			
		||||
			/* core performance interdependency */
 | 
			
		||||
			cpuid_topo->thread_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 | 
			
		||||
			cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 | 
			
		||||
			cpuid_topo->socket_id = MPIDR_AFFINITY_LEVEL(mpidr, 2);
 | 
			
		||||
			cpuid_topo->package_id = MPIDR_AFFINITY_LEVEL(mpidr, 2);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* largely independent cores */
 | 
			
		||||
			cpuid_topo->thread_id = -1;
 | 
			
		||||
			cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 | 
			
		||||
			cpuid_topo->socket_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 | 
			
		||||
			cpuid_topo->package_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/*
 | 
			
		||||
@ -265,7 +228,7 @@ void store_cpu_topology(unsigned int cpuid)
 | 
			
		||||
		 */
 | 
			
		||||
		cpuid_topo->thread_id = -1;
 | 
			
		||||
		cpuid_topo->core_id = 0;
 | 
			
		||||
		cpuid_topo->socket_id = -1;
 | 
			
		||||
		cpuid_topo->package_id = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	update_siblings_masks(cpuid);
 | 
			
		||||
@ -275,7 +238,7 @@ void store_cpu_topology(unsigned int cpuid)
 | 
			
		||||
	pr_info("CPU%u: thread %d, cpu %d, socket %d, mpidr %x\n",
 | 
			
		||||
		cpuid, cpu_topology[cpuid].thread_id,
 | 
			
		||||
		cpu_topology[cpuid].core_id,
 | 
			
		||||
		cpu_topology[cpuid].socket_id, mpidr);
 | 
			
		||||
		cpu_topology[cpuid].package_id, mpidr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int cpu_corepower_flags(void)
 | 
			
		||||
@ -298,18 +261,7 @@ static struct sched_domain_topology_level arm_topology[] = {
 | 
			
		||||
 */
 | 
			
		||||
void __init init_cpu_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cpu;
 | 
			
		||||
 | 
			
		||||
	/* init core mask and capacity */
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
 | 
			
		||||
 | 
			
		||||
		cpu_topo->thread_id = -1;
 | 
			
		||||
		cpu_topo->core_id =  -1;
 | 
			
		||||
		cpu_topo->socket_id = -1;
 | 
			
		||||
		cpumask_clear(&cpu_topo->core_sibling);
 | 
			
		||||
		cpumask_clear(&cpu_topo->thread_sibling);
 | 
			
		||||
	}
 | 
			
		||||
	reset_cpu_topology();
 | 
			
		||||
	smp_wmb();
 | 
			
		||||
 | 
			
		||||
	parse_dt_topology();
 | 
			
		||||
 | 
			
		||||
@ -148,6 +148,7 @@ config ARM64
 | 
			
		||||
	select HAVE_FAST_GUP
 | 
			
		||||
	select HAVE_FTRACE_MCOUNT_RECORD
 | 
			
		||||
	select HAVE_FUNCTION_TRACER
 | 
			
		||||
	select HAVE_FUNCTION_ERROR_INJECTION
 | 
			
		||||
	select HAVE_FUNCTION_GRAPH_TRACER
 | 
			
		||||
	select HAVE_GCC_PLUGINS
 | 
			
		||||
	select HAVE_HW_BREAKPOINT if PERF_EVENTS
 | 
			
		||||
@ -286,7 +287,7 @@ config PGTABLE_LEVELS
 | 
			
		||||
	int
 | 
			
		||||
	default 2 if ARM64_16K_PAGES && ARM64_VA_BITS_36
 | 
			
		||||
	default 2 if ARM64_64K_PAGES && ARM64_VA_BITS_42
 | 
			
		||||
	default 3 if ARM64_64K_PAGES && (ARM64_VA_BITS_48 || ARM64_USER_VA_BITS_52)
 | 
			
		||||
	default 3 if ARM64_64K_PAGES && (ARM64_VA_BITS_48 || ARM64_VA_BITS_52)
 | 
			
		||||
	default 3 if ARM64_4K_PAGES && ARM64_VA_BITS_39
 | 
			
		||||
	default 3 if ARM64_16K_PAGES && ARM64_VA_BITS_47
 | 
			
		||||
	default 4 if !ARM64_64K_PAGES && ARM64_VA_BITS_48
 | 
			
		||||
@ -297,6 +298,21 @@ config ARCH_SUPPORTS_UPROBES
 | 
			
		||||
config ARCH_PROC_KCORE_TEXT
 | 
			
		||||
	def_bool y
 | 
			
		||||
 | 
			
		||||
config KASAN_SHADOW_OFFSET
 | 
			
		||||
	hex
 | 
			
		||||
	depends on KASAN
 | 
			
		||||
	default 0xdfffa00000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && !KASAN_SW_TAGS
 | 
			
		||||
	default 0xdfffd00000000000 if ARM64_VA_BITS_47 && !KASAN_SW_TAGS
 | 
			
		||||
	default 0xdffffe8000000000 if ARM64_VA_BITS_42 && !KASAN_SW_TAGS
 | 
			
		||||
	default 0xdfffffd000000000 if ARM64_VA_BITS_39 && !KASAN_SW_TAGS
 | 
			
		||||
	default 0xdffffffa00000000 if ARM64_VA_BITS_36 && !KASAN_SW_TAGS
 | 
			
		||||
	default 0xefff900000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && KASAN_SW_TAGS
 | 
			
		||||
	default 0xefffc80000000000 if ARM64_VA_BITS_47 && KASAN_SW_TAGS
 | 
			
		||||
	default 0xeffffe4000000000 if ARM64_VA_BITS_42 && KASAN_SW_TAGS
 | 
			
		||||
	default 0xefffffc800000000 if ARM64_VA_BITS_39 && KASAN_SW_TAGS
 | 
			
		||||
	default 0xeffffff900000000 if ARM64_VA_BITS_36 && KASAN_SW_TAGS
 | 
			
		||||
	default 0xffffffffffffffff
 | 
			
		||||
 | 
			
		||||
source "arch/arm64/Kconfig.platforms"
 | 
			
		||||
 | 
			
		||||
menu "Kernel Features"
 | 
			
		||||
@ -744,13 +760,14 @@ config ARM64_VA_BITS_47
 | 
			
		||||
config ARM64_VA_BITS_48
 | 
			
		||||
	bool "48-bit"
 | 
			
		||||
 | 
			
		||||
config ARM64_USER_VA_BITS_52
 | 
			
		||||
	bool "52-bit (user)"
 | 
			
		||||
config ARM64_VA_BITS_52
 | 
			
		||||
	bool "52-bit"
 | 
			
		||||
	depends on ARM64_64K_PAGES && (ARM64_PAN || !ARM64_SW_TTBR0_PAN)
 | 
			
		||||
	help
 | 
			
		||||
	  Enable 52-bit virtual addressing for userspace when explicitly
 | 
			
		||||
	  requested via a hint to mmap(). The kernel will continue to
 | 
			
		||||
	  use 48-bit virtual addresses for its own mappings.
 | 
			
		||||
	  requested via a hint to mmap(). The kernel will also use 52-bit
 | 
			
		||||
	  virtual addresses for its own mappings (provided HW support for
 | 
			
		||||
	  this feature is available, otherwise it reverts to 48-bit).
 | 
			
		||||
 | 
			
		||||
	  NOTE: Enabling 52-bit virtual addressing in conjunction with
 | 
			
		||||
	  ARMv8.3 Pointer Authentication will result in the PAC being
 | 
			
		||||
@ -763,7 +780,7 @@ endchoice
 | 
			
		||||
 | 
			
		||||
config ARM64_FORCE_52BIT
 | 
			
		||||
	bool "Force 52-bit virtual addresses for userspace"
 | 
			
		||||
	depends on ARM64_USER_VA_BITS_52 && EXPERT
 | 
			
		||||
	depends on ARM64_VA_BITS_52 && EXPERT
 | 
			
		||||
	help
 | 
			
		||||
	  For systems with 52-bit userspace VAs enabled, the kernel will attempt
 | 
			
		||||
	  to maintain compatibility with older software by providing 48-bit VAs
 | 
			
		||||
@ -780,7 +797,8 @@ config ARM64_VA_BITS
 | 
			
		||||
	default 39 if ARM64_VA_BITS_39
 | 
			
		||||
	default 42 if ARM64_VA_BITS_42
 | 
			
		||||
	default 47 if ARM64_VA_BITS_47
 | 
			
		||||
	default 48 if ARM64_VA_BITS_48 || ARM64_USER_VA_BITS_52
 | 
			
		||||
	default 48 if ARM64_VA_BITS_48
 | 
			
		||||
	default 52 if ARM64_VA_BITS_52
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Physical address space size"
 | 
			
		||||
@ -1110,6 +1128,15 @@ config ARM64_SW_TTBR0_PAN
 | 
			
		||||
	  zeroed area and reserved ASID. The user access routines
 | 
			
		||||
	  restore the valid TTBR0_EL1 temporarily.
 | 
			
		||||
 | 
			
		||||
config ARM64_TAGGED_ADDR_ABI
 | 
			
		||||
	bool "Enable the tagged user addresses syscall ABI"
 | 
			
		||||
	default y
 | 
			
		||||
	help
 | 
			
		||||
	  When this option is enabled, user applications can opt in to a
 | 
			
		||||
	  relaxed ABI via prctl() allowing tagged addresses to be passed
 | 
			
		||||
	  to system calls as pointer arguments. For details, see
 | 
			
		||||
	  Documentation/arm64/tagged-address-abi.txt.
 | 
			
		||||
 | 
			
		||||
menuconfig COMPAT
 | 
			
		||||
	bool "Kernel support for 32-bit EL0"
 | 
			
		||||
	depends on ARM64_4K_PAGES || EXPERT
 | 
			
		||||
 | 
			
		||||
@ -126,14 +126,6 @@ KBUILD_CFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 | 
			
		||||
KBUILD_CPPFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 | 
			
		||||
KBUILD_AFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 | 
			
		||||
 | 
			
		||||
# KASAN_SHADOW_OFFSET = VA_START + (1 << (VA_BITS - KASAN_SHADOW_SCALE_SHIFT))
 | 
			
		||||
#				 - (1 << (64 - KASAN_SHADOW_SCALE_SHIFT))
 | 
			
		||||
# in 32-bit arithmetic
 | 
			
		||||
KASAN_SHADOW_OFFSET := $(shell printf "0x%08x00000000\n" $$(( \
 | 
			
		||||
	(0xffffffff & (-1 << ($(CONFIG_ARM64_VA_BITS) - 32))) \
 | 
			
		||||
	+ (1 << ($(CONFIG_ARM64_VA_BITS) - 32 - $(KASAN_SHADOW_SCALE_SHIFT))) \
 | 
			
		||||
	- (1 << (64 - 32 - $(KASAN_SHADOW_SCALE_SHIFT))) )) )
 | 
			
		||||
 | 
			
		||||
export	TEXT_OFFSET GZFLAGS
 | 
			
		||||
 | 
			
		||||
core-y		+= arch/arm64/
 | 
			
		||||
 | 
			
		||||
@ -338,6 +338,13 @@ alternative_endif
 | 
			
		||||
	bfi	\valreg, \t0sz, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
 | 
			
		||||
	.endm
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * tcr_set_t1sz - update TCR.T1SZ
 | 
			
		||||
 */
 | 
			
		||||
	.macro	tcr_set_t1sz, valreg, t1sz
 | 
			
		||||
	bfi	\valreg, \t1sz, #TCR_T1SZ_OFFSET, #TCR_TxSZ_WIDTH
 | 
			
		||||
	.endm
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * tcr_compute_pa_size - set TCR.(I)PS to the highest supported
 | 
			
		||||
 * ID_AA64MMFR0_EL1.PARange value
 | 
			
		||||
@ -527,9 +534,13 @@ USER(\label, ic	ivau, \tmp2)			// invalidate I line PoU
 | 
			
		||||
 * In future this may be nop'ed out when dealing with 52-bit kernel VAs.
 | 
			
		||||
 * 	ttbr: Value of ttbr to set, modified.
 | 
			
		||||
 */
 | 
			
		||||
	.macro	offset_ttbr1, ttbr
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
	.macro	offset_ttbr1, ttbr, tmp
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
	mrs_s	\tmp, SYS_ID_AA64MMFR2_EL1
 | 
			
		||||
	and	\tmp, \tmp, #(0xf << ID_AA64MMFR2_LVA_SHIFT)
 | 
			
		||||
	cbnz	\tmp, .Lskipoffs_\@
 | 
			
		||||
	orr	\ttbr, \ttbr, #TTBR1_BADDR_4852_OFFSET
 | 
			
		||||
.Lskipoffs_\@ :
 | 
			
		||||
#endif
 | 
			
		||||
	.endm
 | 
			
		||||
 | 
			
		||||
@ -539,7 +550,7 @@ USER(\label, ic	ivau, \tmp2)			// invalidate I line PoU
 | 
			
		||||
 * to be nop'ed out when dealing with 52-bit kernel VAs.
 | 
			
		||||
 */
 | 
			
		||||
	.macro	restore_ttbr1, ttbr
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
	bic	\ttbr, \ttbr, #TTBR1_BADDR_4852_OFFSET
 | 
			
		||||
#endif
 | 
			
		||||
	.endm
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,8 @@
 | 
			
		||||
 * @cpu_boot:	Boots a cpu into the kernel.
 | 
			
		||||
 * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary
 | 
			
		||||
 *		synchronisation. Called from the cpu being booted.
 | 
			
		||||
 * @cpu_can_disable: Determines whether a CPU can be disabled based on
 | 
			
		||||
 *		mechanism-specific information.
 | 
			
		||||
 * @cpu_disable: Prepares a cpu to die. May fail for some mechanism-specific
 | 
			
		||||
 * 		reason, which will cause the hot unplug to be aborted. Called
 | 
			
		||||
 * 		from the cpu to be killed.
 | 
			
		||||
@ -42,6 +44,7 @@ struct cpu_operations {
 | 
			
		||||
	int		(*cpu_boot)(unsigned int);
 | 
			
		||||
	void		(*cpu_postboot)(void);
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
	bool		(*cpu_can_disable)(unsigned int cpu);
 | 
			
		||||
	int		(*cpu_disable)(unsigned int cpu);
 | 
			
		||||
	void		(*cpu_die)(unsigned int cpu);
 | 
			
		||||
	int		(*cpu_kill)(unsigned int cpu);
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * On arm64, we have to ensure that the initrd ends up in the linear region,
 | 
			
		||||
 * which is a 1 GB aligned region of size '1UL << (VA_BITS - 1)' that is
 | 
			
		||||
 * which is a 1 GB aligned region of size '1UL << (VA_BITS_MIN - 1)' that is
 | 
			
		||||
 * guaranteed to cover the kernel Image.
 | 
			
		||||
 *
 | 
			
		||||
 * Since the EFI stub is part of the kernel Image, we can relax the
 | 
			
		||||
@ -90,7 +90,7 @@ static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base)
 | 
			
		||||
static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 | 
			
		||||
						    unsigned long image_addr)
 | 
			
		||||
{
 | 
			
		||||
	return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS - 1));
 | 
			
		||||
	return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
@ -18,11 +18,8 @@
 | 
			
		||||
 * KASAN_SHADOW_START: beginning of the kernel virtual addresses.
 | 
			
		||||
 * KASAN_SHADOW_END: KASAN_SHADOW_START + 1/N of kernel virtual addresses,
 | 
			
		||||
 * where N = (1 << KASAN_SHADOW_SCALE_SHIFT).
 | 
			
		||||
 */
 | 
			
		||||
#define KASAN_SHADOW_START      (VA_START)
 | 
			
		||||
#define KASAN_SHADOW_END        (KASAN_SHADOW_START + KASAN_SHADOW_SIZE)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * KASAN_SHADOW_OFFSET:
 | 
			
		||||
 * This value is used to map an address to the corresponding shadow
 | 
			
		||||
 * address by the following formula:
 | 
			
		||||
 *     shadow_addr = (address >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET
 | 
			
		||||
@ -33,8 +30,8 @@
 | 
			
		||||
 *      KASAN_SHADOW_OFFSET = KASAN_SHADOW_END -
 | 
			
		||||
 *				(1ULL << (64 - KASAN_SHADOW_SCALE_SHIFT))
 | 
			
		||||
 */
 | 
			
		||||
#define KASAN_SHADOW_OFFSET     (KASAN_SHADOW_END - (1ULL << \
 | 
			
		||||
					(64 - KASAN_SHADOW_SCALE_SHIFT)))
 | 
			
		||||
#define _KASAN_SHADOW_START(va)	(KASAN_SHADOW_END - (1UL << ((va) - KASAN_SHADOW_SCALE_SHIFT)))
 | 
			
		||||
#define KASAN_SHADOW_START      _KASAN_SHADOW_START(vabits_actual)
 | 
			
		||||
 | 
			
		||||
void kasan_init(void);
 | 
			
		||||
void kasan_copy_shadow(pgd_t *pgdir);
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,10 @@
 | 
			
		||||
 | 
			
		||||
#include <linux/compiler.h>
 | 
			
		||||
#include <linux/const.h>
 | 
			
		||||
#include <linux/sizes.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <asm/bug.h>
 | 
			
		||||
#include <asm/page-def.h>
 | 
			
		||||
#include <linux/sizes.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Size of the PCI I/O space. This must remain a power of two so that
 | 
			
		||||
@ -26,37 +26,50 @@
 | 
			
		||||
/*
 | 
			
		||||
 * VMEMMAP_SIZE - allows the whole linear region to be covered by
 | 
			
		||||
 *                a struct page array
 | 
			
		||||
 *
 | 
			
		||||
 * If we are configured with a 52-bit kernel VA then our VMEMMAP_SIZE
 | 
			
		||||
 * needs to cover the memory region from the beginning of the 52-bit
 | 
			
		||||
 * PAGE_OFFSET all the way to PAGE_END for 48-bit. This allows us to
 | 
			
		||||
 * keep a constant PAGE_OFFSET and "fallback" to using the higher end
 | 
			
		||||
 * of the VMEMMAP where 52-bit support is not available in hardware.
 | 
			
		||||
 */
 | 
			
		||||
#define VMEMMAP_SIZE (UL(1) << (VA_BITS - PAGE_SHIFT - 1 + STRUCT_PAGE_MAX_SHIFT))
 | 
			
		||||
#define VMEMMAP_SIZE ((_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) \
 | 
			
		||||
			>> (PAGE_SHIFT - STRUCT_PAGE_MAX_SHIFT))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PAGE_OFFSET - the virtual address of the start of the linear map (top
 | 
			
		||||
 *		 (VA_BITS - 1))
 | 
			
		||||
 * KIMAGE_VADDR - the virtual address of the start of the kernel image
 | 
			
		||||
 * PAGE_OFFSET - the virtual address of the start of the linear map, at the
 | 
			
		||||
 *               start of the TTBR1 address space.
 | 
			
		||||
 * PAGE_END - the end of the linear map, where all other kernel mappings begin.
 | 
			
		||||
 * KIMAGE_VADDR - the virtual address of the start of the kernel image.
 | 
			
		||||
 * VA_BITS - the maximum number of bits for virtual addresses.
 | 
			
		||||
 * VA_START - the first kernel virtual address.
 | 
			
		||||
 */
 | 
			
		||||
#define VA_BITS			(CONFIG_ARM64_VA_BITS)
 | 
			
		||||
#define VA_START		(UL(0xffffffffffffffff) - \
 | 
			
		||||
	(UL(1) << VA_BITS) + 1)
 | 
			
		||||
#define PAGE_OFFSET		(UL(0xffffffffffffffff) - \
 | 
			
		||||
	(UL(1) << (VA_BITS - 1)) + 1)
 | 
			
		||||
#define _PAGE_OFFSET(va)	(-(UL(1) << (va)))
 | 
			
		||||
#define PAGE_OFFSET		(_PAGE_OFFSET(VA_BITS))
 | 
			
		||||
#define KIMAGE_VADDR		(MODULES_END)
 | 
			
		||||
#define BPF_JIT_REGION_START	(VA_START + KASAN_SHADOW_SIZE)
 | 
			
		||||
#define BPF_JIT_REGION_START	(KASAN_SHADOW_END)
 | 
			
		||||
#define BPF_JIT_REGION_SIZE	(SZ_128M)
 | 
			
		||||
#define BPF_JIT_REGION_END	(BPF_JIT_REGION_START + BPF_JIT_REGION_SIZE)
 | 
			
		||||
#define MODULES_END		(MODULES_VADDR + MODULES_VSIZE)
 | 
			
		||||
#define MODULES_VADDR		(BPF_JIT_REGION_END)
 | 
			
		||||
#define MODULES_VSIZE		(SZ_128M)
 | 
			
		||||
#define VMEMMAP_START		(PAGE_OFFSET - VMEMMAP_SIZE)
 | 
			
		||||
#define VMEMMAP_START		(-VMEMMAP_SIZE - SZ_2M)
 | 
			
		||||
#define PCI_IO_END		(VMEMMAP_START - SZ_2M)
 | 
			
		||||
#define PCI_IO_START		(PCI_IO_END - PCI_IO_SIZE)
 | 
			
		||||
#define FIXADDR_TOP		(PCI_IO_START - SZ_2M)
 | 
			
		||||
 | 
			
		||||
#define KERNEL_START      _text
 | 
			
		||||
#define KERNEL_END        _end
 | 
			
		||||
#if VA_BITS > 48
 | 
			
		||||
#define VA_BITS_MIN		(48)
 | 
			
		||||
#else
 | 
			
		||||
#define VA_BITS_MIN		(VA_BITS)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
#define _PAGE_END(va)		(-(UL(1) << ((va) - 1)))
 | 
			
		||||
 | 
			
		||||
#define KERNEL_START		_text
 | 
			
		||||
#define KERNEL_END		_end
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
#define MAX_USER_VA_BITS	52
 | 
			
		||||
#else
 | 
			
		||||
#define MAX_USER_VA_BITS	VA_BITS
 | 
			
		||||
@ -68,12 +81,14 @@
 | 
			
		||||
 * significantly, so double the (minimum) stack size when they are in use.
 | 
			
		||||
 */
 | 
			
		||||
#ifdef CONFIG_KASAN
 | 
			
		||||
#define KASAN_SHADOW_SIZE	(UL(1) << (VA_BITS - KASAN_SHADOW_SCALE_SHIFT))
 | 
			
		||||
#define KASAN_SHADOW_OFFSET	_AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
 | 
			
		||||
#define KASAN_SHADOW_END	((UL(1) << (64 - KASAN_SHADOW_SCALE_SHIFT)) \
 | 
			
		||||
					+ KASAN_SHADOW_OFFSET)
 | 
			
		||||
#define KASAN_THREAD_SHIFT	1
 | 
			
		||||
#else
 | 
			
		||||
#define KASAN_SHADOW_SIZE	(0)
 | 
			
		||||
#define KASAN_THREAD_SHIFT	0
 | 
			
		||||
#endif
 | 
			
		||||
#define KASAN_SHADOW_END	(_PAGE_END(VA_BITS_MIN))
 | 
			
		||||
#endif /* CONFIG_KASAN */
 | 
			
		||||
 | 
			
		||||
#define MIN_THREAD_SHIFT	(14 + KASAN_THREAD_SHIFT)
 | 
			
		||||
 | 
			
		||||
@ -117,14 +132,14 @@
 | 
			
		||||
 * 16 KB granule: 128 level 3 entries, with contiguous bit
 | 
			
		||||
 * 64 KB granule:  32 level 3 entries, with contiguous bit
 | 
			
		||||
 */
 | 
			
		||||
#define SEGMENT_ALIGN			SZ_2M
 | 
			
		||||
#define SEGMENT_ALIGN		SZ_2M
 | 
			
		||||
#else
 | 
			
		||||
/*
 | 
			
		||||
 *  4 KB granule:  16 level 3 entries, with contiguous bit
 | 
			
		||||
 * 16 KB granule:   4 level 3 entries, without contiguous bit
 | 
			
		||||
 * 64 KB granule:   1 level 3 entry
 | 
			
		||||
 */
 | 
			
		||||
#define SEGMENT_ALIGN			SZ_64K
 | 
			
		||||
#define SEGMENT_ALIGN		SZ_64K
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -157,10 +172,13 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
extern u64			vabits_actual;
 | 
			
		||||
#define PAGE_END		(_PAGE_END(vabits_actual))
 | 
			
		||||
 | 
			
		||||
#include <linux/bitops.h>
 | 
			
		||||
#include <linux/mmdebug.h>
 | 
			
		||||
 | 
			
		||||
extern s64			physvirt_offset;
 | 
			
		||||
extern s64			memstart_addr;
 | 
			
		||||
/* PHYS_OFFSET - the physical address of the start of memory. */
 | 
			
		||||
#define PHYS_OFFSET		({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })
 | 
			
		||||
@ -176,9 +194,6 @@ static inline unsigned long kaslr_offset(void)
 | 
			
		||||
	return kimage_vaddr - KIMAGE_VADDR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* the actual size of a user virtual address */
 | 
			
		||||
extern u64			vabits_user;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Allow all memory at the discovery stage. We will clip it later.
 | 
			
		||||
 */
 | 
			
		||||
@ -201,23 +216,23 @@ extern u64			vabits_user;
 | 
			
		||||
 * pass on to access_ok(), for instance.
 | 
			
		||||
 */
 | 
			
		||||
#define untagged_addr(addr)	\
 | 
			
		||||
	((__typeof__(addr))sign_extend64((u64)(addr), 55))
 | 
			
		||||
	((__force __typeof__(addr))sign_extend64((__force u64)(addr), 55))
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_KASAN_SW_TAGS
 | 
			
		||||
#define __tag_shifted(tag)	((u64)(tag) << 56)
 | 
			
		||||
#define __tag_set(addr, tag)	(__typeof__(addr))( \
 | 
			
		||||
		((u64)(addr) & ~__tag_shifted(0xff)) | __tag_shifted(tag))
 | 
			
		||||
#define __tag_reset(addr)	untagged_addr(addr)
 | 
			
		||||
#define __tag_get(addr)		(__u8)((u64)(addr) >> 56)
 | 
			
		||||
#else
 | 
			
		||||
static inline const void *__tag_set(const void *addr, u8 tag)
 | 
			
		||||
{
 | 
			
		||||
	return addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define __tag_shifted(tag)	0UL
 | 
			
		||||
#define __tag_reset(addr)	(addr)
 | 
			
		||||
#define __tag_get(addr)		0
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_KASAN_SW_TAGS */
 | 
			
		||||
 | 
			
		||||
static inline const void *__tag_set(const void *addr, u8 tag)
 | 
			
		||||
{
 | 
			
		||||
	u64 __addr = (u64)addr & ~__tag_shifted(0xff);
 | 
			
		||||
	return (const void *)(__addr | __tag_shifted(tag));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Physical vs virtual RAM address space conversion.  These are
 | 
			
		||||
@ -227,19 +242,18 @@ static inline const void *__tag_set(const void *addr, u8 tag)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The linear kernel range starts in the middle of the virtual adddress
 | 
			
		||||
 * The linear kernel range starts at the bottom of the virtual address
 | 
			
		||||
 * space. Testing the top bit for the start of the region is a
 | 
			
		||||
 * sufficient check.
 | 
			
		||||
 * sufficient check and avoids having to worry about the tag.
 | 
			
		||||
 */
 | 
			
		||||
#define __is_lm_address(addr)	(!!((addr) & BIT(VA_BITS - 1)))
 | 
			
		||||
#define __is_lm_address(addr)	(!(((u64)addr) & BIT(vabits_actual - 1)))
 | 
			
		||||
 | 
			
		||||
#define __lm_to_phys(addr)	(((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)
 | 
			
		||||
#define __lm_to_phys(addr)	(((addr) + physvirt_offset))
 | 
			
		||||
#define __kimg_to_phys(addr)	((addr) - kimage_voffset)
 | 
			
		||||
 | 
			
		||||
#define __virt_to_phys_nodebug(x) ({					\
 | 
			
		||||
	phys_addr_t __x = (phys_addr_t)(x);				\
 | 
			
		||||
	__is_lm_address(__x) ? __lm_to_phys(__x) :			\
 | 
			
		||||
			       __kimg_to_phys(__x);			\
 | 
			
		||||
	phys_addr_t __x = (phys_addr_t)(__tag_reset(x));		\
 | 
			
		||||
	__is_lm_address(__x) ? __lm_to_phys(__x) : __kimg_to_phys(__x);	\
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
#define __pa_symbol_nodebug(x)	__kimg_to_phys((phys_addr_t)(x))
 | 
			
		||||
@ -250,9 +264,9 @@ extern phys_addr_t __phys_addr_symbol(unsigned long x);
 | 
			
		||||
#else
 | 
			
		||||
#define __virt_to_phys(x)	__virt_to_phys_nodebug(x)
 | 
			
		||||
#define __phys_addr_symbol(x)	__pa_symbol_nodebug(x)
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_DEBUG_VIRTUAL */
 | 
			
		||||
 | 
			
		||||
#define __phys_to_virt(x)	((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET)
 | 
			
		||||
#define __phys_to_virt(x)	((unsigned long)((x) - physvirt_offset))
 | 
			
		||||
#define __phys_to_kimg(x)	((unsigned long)((x) + kimage_voffset))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -286,41 +300,38 @@ static inline void *phys_to_virt(phys_addr_t x)
 | 
			
		||||
#define __pa_nodebug(x)		__virt_to_phys_nodebug((unsigned long)(x))
 | 
			
		||||
#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))
 | 
			
		||||
#define pfn_to_kaddr(pfn)	__va((pfn) << PAGE_SHIFT)
 | 
			
		||||
#define virt_to_pfn(x)      __phys_to_pfn(__virt_to_phys((unsigned long)(x)))
 | 
			
		||||
#define sym_to_pfn(x)	    __phys_to_pfn(__pa_symbol(x))
 | 
			
		||||
#define virt_to_pfn(x)		__phys_to_pfn(__virt_to_phys((unsigned long)(x)))
 | 
			
		||||
#define sym_to_pfn(x)		__phys_to_pfn(__pa_symbol(x))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  virt_to_page(k)	convert a _valid_ virtual address to struct page *
 | 
			
		||||
 *  virt_addr_valid(k)	indicates whether a virtual address is valid
 | 
			
		||||
 *  virt_to_page(x)	convert a _valid_ virtual address to struct page *
 | 
			
		||||
 *  virt_addr_valid(x)	indicates whether a virtual address is valid
 | 
			
		||||
 */
 | 
			
		||||
#define ARCH_PFN_OFFSET		((unsigned long)PHYS_PFN_OFFSET)
 | 
			
		||||
 | 
			
		||||
#if !defined(CONFIG_SPARSEMEM_VMEMMAP) || defined(CONFIG_DEBUG_VIRTUAL)
 | 
			
		||||
#define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
 | 
			
		||||
#define _virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 | 
			
		||||
#define virt_to_page(x)		pfn_to_page(virt_to_pfn(x))
 | 
			
		||||
#else
 | 
			
		||||
#define __virt_to_pgoff(kaddr)	(((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
 | 
			
		||||
#define __page_to_voff(kaddr)	(((u64)(kaddr) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
 | 
			
		||||
 | 
			
		||||
#define page_to_virt(page)	({					\
 | 
			
		||||
	unsigned long __addr =						\
 | 
			
		||||
		((__page_to_voff(page)) | PAGE_OFFSET);			\
 | 
			
		||||
	const void *__addr_tag =					\
 | 
			
		||||
		__tag_set((void *)__addr, page_kasan_tag(page));	\
 | 
			
		||||
	((void *)__addr_tag);						\
 | 
			
		||||
#define page_to_virt(x)	({						\
 | 
			
		||||
	__typeof__(x) __page = x;					\
 | 
			
		||||
	u64 __idx = ((u64)__page - VMEMMAP_START) / sizeof(struct page);\
 | 
			
		||||
	u64 __addr = PAGE_OFFSET + (__idx * PAGE_SIZE);			\
 | 
			
		||||
	(void *)__tag_set((const void *)__addr, page_kasan_tag(__page));\
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
#define virt_to_page(vaddr)	((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
 | 
			
		||||
#define virt_to_page(x)	({						\
 | 
			
		||||
	u64 __idx = (__tag_reset((u64)x) - PAGE_OFFSET) / PAGE_SIZE;	\
 | 
			
		||||
	u64 __addr = VMEMMAP_START + (__idx * sizeof(struct page));	\
 | 
			
		||||
	(struct page *)__addr;						\
 | 
			
		||||
})
 | 
			
		||||
#endif /* !CONFIG_SPARSEMEM_VMEMMAP || CONFIG_DEBUG_VIRTUAL */
 | 
			
		||||
 | 
			
		||||
#define _virt_addr_valid(kaddr)	pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
 | 
			
		||||
					   + PHYS_OFFSET) >> PAGE_SHIFT)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#define virt_addr_valid(addr)	({					\
 | 
			
		||||
	__typeof__(addr) __addr = addr;					\
 | 
			
		||||
	__is_lm_address(__addr) && pfn_valid(virt_to_pfn(__addr));	\
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
#define _virt_addr_is_linear(kaddr)	\
 | 
			
		||||
	(__tag_reset((u64)(kaddr)) >= PAGE_OFFSET)
 | 
			
		||||
#define virt_addr_valid(kaddr)		\
 | 
			
		||||
	(_virt_addr_is_linear(kaddr) && _virt_addr_valid(kaddr))
 | 
			
		||||
#endif /* !ASSEMBLY */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Given that the GIC architecture permits ITS implementations that can only be
 | 
			
		||||
@ -335,4 +346,4 @@ static inline void *phys_to_virt(phys_addr_t x)
 | 
			
		||||
 | 
			
		||||
#include <asm-generic/memory_model.h>
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* __ASM_MEMORY_H */
 | 
			
		||||
 | 
			
		||||
@ -126,7 +126,7 @@ extern void init_mem_pgprot(void);
 | 
			
		||||
extern void 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);
 | 
			
		||||
extern void *fixmap_remap_fdt(phys_addr_t dt_phys);
 | 
			
		||||
extern void *fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot);
 | 
			
		||||
extern void mark_linear_text_alias_ro(void);
 | 
			
		||||
 | 
			
		||||
#define INIT_MM_CONTEXT(name)	\
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ extern u64 idmap_ptrs_per_pgd;
 | 
			
		||||
 | 
			
		||||
static inline bool __cpu_uses_extended_idmap(void)
 | 
			
		||||
{
 | 
			
		||||
	if (IS_ENABLED(CONFIG_ARM64_USER_VA_BITS_52))
 | 
			
		||||
	if (IS_ENABLED(CONFIG_ARM64_VA_BITS_52))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS));
 | 
			
		||||
@ -95,7 +95,7 @@ static inline void __cpu_set_tcr_t0sz(unsigned long t0sz)
 | 
			
		||||
	isb();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define cpu_set_default_tcr_t0sz()	__cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS))
 | 
			
		||||
#define cpu_set_default_tcr_t0sz()	__cpu_set_tcr_t0sz(TCR_T0SZ(vabits_actual))
 | 
			
		||||
#define cpu_set_idmap_tcr_t0sz()	__cpu_set_tcr_t0sz(idmap_t0sz)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
@ -304,7 +304,7 @@
 | 
			
		||||
#define TTBR_BADDR_MASK_52	(((UL(1) << 46) - 1) << 2)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
/* Must be at least 64-byte aligned to prevent corruption of the TTBR */
 | 
			
		||||
#define TTBR1_BADDR_4852_OFFSET	(((UL(1) << (52 - PGDIR_SHIFT)) - \
 | 
			
		||||
				 (UL(1) << (48 - PGDIR_SHIFT))) * 8)
 | 
			
		||||
 | 
			
		||||
@ -21,9 +21,7 @@
 | 
			
		||||
 *	and fixed mappings
 | 
			
		||||
 */
 | 
			
		||||
#define VMALLOC_START		(MODULES_END)
 | 
			
		||||
#define VMALLOC_END		(PAGE_OFFSET - PUD_SIZE - VMEMMAP_SIZE - SZ_64K)
 | 
			
		||||
 | 
			
		||||
#define vmemmap			((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT))
 | 
			
		||||
#define VMALLOC_END		(- PUD_SIZE - VMEMMAP_SIZE - SZ_64K)
 | 
			
		||||
 | 
			
		||||
#define FIRST_USER_ADDRESS	0UL
 | 
			
		||||
 | 
			
		||||
@ -35,6 +33,8 @@
 | 
			
		||||
#include <linux/mm_types.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
 | 
			
		||||
extern struct page *vmemmap;
 | 
			
		||||
 | 
			
		||||
extern void __pte_error(const char *file, int line, unsigned long val);
 | 
			
		||||
extern void __pmd_error(const char *file, int line, unsigned long val);
 | 
			
		||||
extern void __pud_error(const char *file, int line, unsigned long val);
 | 
			
		||||
@ -220,8 +220,10 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
 | 
			
		||||
	 * Only if the new pte is valid and kernel, otherwise TLB maintenance
 | 
			
		||||
	 * or update_mmu_cache() have the necessary barriers.
 | 
			
		||||
	 */
 | 
			
		||||
	if (pte_valid_not_user(pte))
 | 
			
		||||
	if (pte_valid_not_user(pte)) {
 | 
			
		||||
		dsb(ishst);
 | 
			
		||||
		isb();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void __sync_icache_dcache(pte_t pteval);
 | 
			
		||||
@ -481,8 +483,10 @@ static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
 | 
			
		||||
 | 
			
		||||
	WRITE_ONCE(*pmdp, pmd);
 | 
			
		||||
 | 
			
		||||
	if (pmd_valid(pmd))
 | 
			
		||||
	if (pmd_valid(pmd)) {
 | 
			
		||||
		dsb(ishst);
 | 
			
		||||
		isb();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void pmd_clear(pmd_t *pmdp)
 | 
			
		||||
@ -540,8 +544,10 @@ static inline void set_pud(pud_t *pudp, pud_t pud)
 | 
			
		||||
 | 
			
		||||
	WRITE_ONCE(*pudp, pud);
 | 
			
		||||
 | 
			
		||||
	if (pud_valid(pud))
 | 
			
		||||
	if (pud_valid(pud)) {
 | 
			
		||||
		dsb(ishst);
 | 
			
		||||
		isb();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void pud_clear(pud_t *pudp)
 | 
			
		||||
@ -599,6 +605,7 @@ static inline void set_pgd(pgd_t *pgdp, pgd_t pgd)
 | 
			
		||||
 | 
			
		||||
	WRITE_ONCE(*pgdp, pgd);
 | 
			
		||||
	dsb(ishst);
 | 
			
		||||
	isb();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void pgd_clear(pgd_t *pgdp)
 | 
			
		||||
@ -856,8 +863,8 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
 | 
			
		||||
 | 
			
		||||
#define update_mmu_cache_pmd(vma, address, pmd) do { } while (0)
 | 
			
		||||
 | 
			
		||||
#define kc_vaddr_to_offset(v)	((v) & ~VA_START)
 | 
			
		||||
#define kc_offset_to_vaddr(o)	((o) | VA_START)
 | 
			
		||||
#define kc_vaddr_to_offset(v)	((v) & ~PAGE_END)
 | 
			
		||||
#define kc_offset_to_vaddr(o)	((o) | PAGE_END)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_PA_BITS_52
 | 
			
		||||
#define phys_to_ttbr(addr)	(((addr) | ((addr) >> 46)) & TTBR_BADDR_MASK_52)
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@ extern int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg);
 | 
			
		||||
 * The EL0 pointer bits used by a pointer authentication code.
 | 
			
		||||
 * This is dependent on TBI0 being enabled, or bits 63:56 would also apply.
 | 
			
		||||
 */
 | 
			
		||||
#define ptrauth_user_pac_mask()	GENMASK(54, vabits_user)
 | 
			
		||||
#define ptrauth_user_pac_mask()	GENMASK(54, vabits_actual)
 | 
			
		||||
 | 
			
		||||
/* Only valid for EL0 TTBR0 instruction pointers */
 | 
			
		||||
static inline unsigned long ptrauth_strip_insn_pac(unsigned long ptr)
 | 
			
		||||
 | 
			
		||||
@ -41,8 +41,8 @@
 | 
			
		||||
 * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_MAP_WINDOW_64	(UL(1) << VA_BITS)
 | 
			
		||||
#define TASK_SIZE_64		(UL(1) << vabits_user)
 | 
			
		||||
#define DEFAULT_MAP_WINDOW_64	(UL(1) << VA_BITS_MIN)
 | 
			
		||||
#define TASK_SIZE_64		(UL(1) << vabits_actual)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_COMPAT
 | 
			
		||||
#if defined(CONFIG_ARM64_64K_PAGES) && defined(CONFIG_KUSER_HELPERS)
 | 
			
		||||
@ -303,6 +303,14 @@ extern void __init minsigstksz_setup(void);
 | 
			
		||||
/* PR_PAC_RESET_KEYS prctl */
 | 
			
		||||
#define PAC_RESET_KEYS(tsk, arg)	ptrauth_prctl_reset_keys(tsk, arg)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
 | 
			
		||||
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
 | 
			
		||||
long set_tagged_addr_ctrl(unsigned long arg);
 | 
			
		||||
long get_tagged_addr_ctrl(void);
 | 
			
		||||
#define SET_TAGGED_ADDR_CTRL(arg)	set_tagged_addr_ctrl(arg)
 | 
			
		||||
#define GET_TAGGED_ADDR_CTRL()		get_tagged_addr_ctrl()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For CONFIG_GCC_PLUGIN_STACKLEAK
 | 
			
		||||
 *
 | 
			
		||||
 | 
			
		||||
@ -301,6 +301,11 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
 | 
			
		||||
	return regs->regs[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
 | 
			
		||||
{
 | 
			
		||||
	regs->regs[0] = rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * regs_get_kernel_argument() - get Nth function argument in kernel
 | 
			
		||||
 * @regs:	pt_regs of that context
 | 
			
		||||
 | 
			
		||||
@ -212,6 +212,9 @@
 | 
			
		||||
#define SYS_FAR_EL1			sys_reg(3, 0, 6, 0, 0)
 | 
			
		||||
#define SYS_PAR_EL1			sys_reg(3, 0, 7, 4, 0)
 | 
			
		||||
 | 
			
		||||
#define SYS_PAR_EL1_F			BIT(1)
 | 
			
		||||
#define SYS_PAR_EL1_FST			GENMASK(6, 1)
 | 
			
		||||
 | 
			
		||||
/*** Statistical Profiling Extension ***/
 | 
			
		||||
/* ID registers */
 | 
			
		||||
#define SYS_PMSIDR_EL1			sys_reg(3, 0, 9, 9, 7)
 | 
			
		||||
 | 
			
		||||
@ -77,6 +77,7 @@ void arch_release_task_struct(struct task_struct *tsk);
 | 
			
		||||
#define TIF_SVE			23	/* Scalable Vector Extension in use */
 | 
			
		||||
#define TIF_SVE_VL_INHERIT	24	/* Inherit sve_vl_onexec across exec */
 | 
			
		||||
#define TIF_SSBD		25	/* Wants SSB mitigation */
 | 
			
		||||
#define TIF_TAGGED_ADDR		26	/* Allow tagged user addresses */
 | 
			
		||||
 | 
			
		||||
#define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 | 
			
		||||
#define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 | 
			
		||||
 | 
			
		||||
@ -251,6 +251,7 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)
 | 
			
		||||
	dsb(ishst);
 | 
			
		||||
	__tlbi(vaae1is, addr);
 | 
			
		||||
	dsb(ish);
 | 
			
		||||
	isb();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,29 +4,6 @@
 | 
			
		||||
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
 | 
			
		||||
struct cpu_topology {
 | 
			
		||||
	int thread_id;
 | 
			
		||||
	int core_id;
 | 
			
		||||
	int package_id;
 | 
			
		||||
	int llc_id;
 | 
			
		||||
	cpumask_t thread_sibling;
 | 
			
		||||
	cpumask_t core_sibling;
 | 
			
		||||
	cpumask_t llc_sibling;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct cpu_topology cpu_topology[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
#define topology_physical_package_id(cpu)	(cpu_topology[cpu].package_id)
 | 
			
		||||
#define topology_core_id(cpu)		(cpu_topology[cpu].core_id)
 | 
			
		||||
#define topology_core_cpumask(cpu)	(&cpu_topology[cpu].core_sibling)
 | 
			
		||||
#define topology_sibling_cpumask(cpu)	(&cpu_topology[cpu].thread_sibling)
 | 
			
		||||
#define topology_llc_cpumask(cpu)	(&cpu_topology[cpu].llc_sibling)
 | 
			
		||||
 | 
			
		||||
void init_cpu_topology(void);
 | 
			
		||||
void store_cpu_topology(unsigned int cpuid);
 | 
			
		||||
void remove_cpu_topology(unsigned int cpuid);
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_NUMA
 | 
			
		||||
 | 
			
		||||
struct pci_bus;
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,10 @@ static inline unsigned long __range_ok(const void __user *addr, unsigned long si
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ret, limit = current_thread_info()->addr_limit;
 | 
			
		||||
 | 
			
		||||
	if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI) &&
 | 
			
		||||
	    test_thread_flag(TIF_TAGGED_ADDR))
 | 
			
		||||
		addr = untagged_addr(addr);
 | 
			
		||||
 | 
			
		||||
	__chk_user_ptr(addr);
 | 
			
		||||
	asm volatile(
 | 
			
		||||
	// A + B <= C + 1 for all A,B,C, in four easy steps:
 | 
			
		||||
@ -215,7 +219,8 @@ static inline void uaccess_enable_not_uao(void)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Sanitise a uaccess pointer such that it becomes NULL if above the
 | 
			
		||||
 * current addr_limit.
 | 
			
		||||
 * current addr_limit. In case the pointer is tagged (has the top byte set),
 | 
			
		||||
 * untag the pointer before checking.
 | 
			
		||||
 */
 | 
			
		||||
#define uaccess_mask_ptr(ptr) (__typeof__(ptr))__uaccess_mask_ptr(ptr)
 | 
			
		||||
static inline void __user *__uaccess_mask_ptr(const void __user *ptr)
 | 
			
		||||
@ -223,10 +228,11 @@ static inline void __user *__uaccess_mask_ptr(const void __user *ptr)
 | 
			
		||||
	void __user *safe_ptr;
 | 
			
		||||
 | 
			
		||||
	asm volatile(
 | 
			
		||||
	"	bics	xzr, %1, %2\n"
 | 
			
		||||
	"	bics	xzr, %3, %2\n"
 | 
			
		||||
	"	csel	%0, %1, xzr, eq\n"
 | 
			
		||||
	: "=&r" (safe_ptr)
 | 
			
		||||
	: "r" (ptr), "r" (current_thread_info()->addr_limit)
 | 
			
		||||
	: "r" (ptr), "r" (current_thread_info()->addr_limit),
 | 
			
		||||
	  "r" (untagged_addr(ptr))
 | 
			
		||||
	: "cc");
 | 
			
		||||
 | 
			
		||||
	csdb();
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@
 | 
			
		||||
#include <linux/cpu_pm.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/of_device.h>
 | 
			
		||||
#include <linux/psci.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/cpuidle.h>
 | 
			
		||||
#include <asm/cpu_ops.h>
 | 
			
		||||
@ -46,17 +47,58 @@ int arm_cpuidle_suspend(int index)
 | 
			
		||||
 | 
			
		||||
#define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags))
 | 
			
		||||
 | 
			
		||||
static int psci_acpi_cpu_init_idle(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int i, count;
 | 
			
		||||
	struct acpi_lpi_state *lpi;
 | 
			
		||||
	struct acpi_processor *pr = per_cpu(processors, cpu);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the PSCI cpu_suspend function hook has not been initialized
 | 
			
		||||
	 * idle states must not be enabled, so bail out
 | 
			
		||||
	 */
 | 
			
		||||
	if (!psci_ops.cpu_suspend)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (unlikely(!pr || !pr->flags.has_lpi))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	count = pr->power.count - 1;
 | 
			
		||||
	if (count <= 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; i++) {
 | 
			
		||||
		u32 state;
 | 
			
		||||
 | 
			
		||||
		lpi = &pr->power.lpi_states[i + 1];
 | 
			
		||||
		/*
 | 
			
		||||
		 * Only bits[31:0] represent a PSCI power_state while
 | 
			
		||||
		 * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
 | 
			
		||||
		 */
 | 
			
		||||
		state = lpi->address;
 | 
			
		||||
		if (!psci_power_state_is_valid(state)) {
 | 
			
		||||
			pr_warn("Invalid PSCI power state %#x\n", state);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int acpi_processor_ffh_lpi_probe(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return arm_cpuidle_init(cpu);
 | 
			
		||||
	return psci_acpi_cpu_init_idle(cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
 | 
			
		||||
{
 | 
			
		||||
	u32 state = lpi->address;
 | 
			
		||||
 | 
			
		||||
	if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags))
 | 
			
		||||
		return CPU_PM_CPU_IDLE_ENTER_RETENTION(arm_cpuidle_suspend,
 | 
			
		||||
						lpi->index);
 | 
			
		||||
		return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(psci_cpu_suspend_enter,
 | 
			
		||||
						lpi->index, state);
 | 
			
		||||
	else
 | 
			
		||||
		return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, lpi->index);
 | 
			
		||||
		return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter,
 | 
			
		||||
					     lpi->index, state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -310,15 +310,15 @@ __create_page_tables:
 | 
			
		||||
	adrp	x0, idmap_pg_dir
 | 
			
		||||
	adrp	x3, __idmap_text_start		// __pa(__idmap_text_start)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
	mrs_s	x6, SYS_ID_AA64MMFR2_EL1
 | 
			
		||||
	and	x6, x6, #(0xf << ID_AA64MMFR2_LVA_SHIFT)
 | 
			
		||||
	mov	x5, #52
 | 
			
		||||
	cbnz	x6, 1f
 | 
			
		||||
#endif
 | 
			
		||||
	mov	x5, #VA_BITS
 | 
			
		||||
	mov	x5, #VA_BITS_MIN
 | 
			
		||||
1:
 | 
			
		||||
	adr_l	x6, vabits_user
 | 
			
		||||
	adr_l	x6, vabits_actual
 | 
			
		||||
	str	x5, [x6]
 | 
			
		||||
	dmb	sy
 | 
			
		||||
	dc	ivac, x6		// Invalidate potentially stale cache line
 | 
			
		||||
@ -726,14 +726,22 @@ __secondary_switched:
 | 
			
		||||
 | 
			
		||||
	adr_l	x0, secondary_data
 | 
			
		||||
	ldr	x1, [x0, #CPU_BOOT_STACK]	// get secondary_data.stack
 | 
			
		||||
	cbz	x1, __secondary_too_slow
 | 
			
		||||
	mov	sp, x1
 | 
			
		||||
	ldr	x2, [x0, #CPU_BOOT_TASK]
 | 
			
		||||
	cbz	x2, __secondary_too_slow
 | 
			
		||||
	msr	sp_el0, x2
 | 
			
		||||
	mov	x29, #0
 | 
			
		||||
	mov	x30, #0
 | 
			
		||||
	b	secondary_start_kernel
 | 
			
		||||
ENDPROC(__secondary_switched)
 | 
			
		||||
 | 
			
		||||
__secondary_too_slow:
 | 
			
		||||
	wfe
 | 
			
		||||
	wfi
 | 
			
		||||
	b	__secondary_too_slow
 | 
			
		||||
ENDPROC(__secondary_too_slow)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The booting CPU updates the failed status @__early_cpu_boot_status,
 | 
			
		||||
 * with MMU turned off.
 | 
			
		||||
@ -774,7 +782,7 @@ ENTRY(__enable_mmu)
 | 
			
		||||
	phys_to_ttbr x1, x1
 | 
			
		||||
	phys_to_ttbr x2, x2
 | 
			
		||||
	msr	ttbr0_el1, x2			// load TTBR0
 | 
			
		||||
	offset_ttbr1 x1
 | 
			
		||||
	offset_ttbr1 x1, x3
 | 
			
		||||
	msr	ttbr1_el1, x1			// load TTBR1
 | 
			
		||||
	isb
 | 
			
		||||
	msr	sctlr_el1, x0
 | 
			
		||||
@ -791,8 +799,8 @@ ENTRY(__enable_mmu)
 | 
			
		||||
ENDPROC(__enable_mmu)
 | 
			
		||||
 | 
			
		||||
ENTRY(__cpu_secondary_check52bitva)
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
	ldr_l	x0, vabits_user
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
	ldr_l	x0, vabits_actual
 | 
			
		||||
	cmp	x0, #52
 | 
			
		||||
	b.ne	2f
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,14 +22,14 @@
 | 
			
		||||
 * Even switching to our copied tables will cause a changed output address at
 | 
			
		||||
 * each stage of the walk.
 | 
			
		||||
 */
 | 
			
		||||
.macro break_before_make_ttbr_switch zero_page, page_table, tmp
 | 
			
		||||
.macro break_before_make_ttbr_switch zero_page, page_table, tmp, tmp2
 | 
			
		||||
	phys_to_ttbr \tmp, \zero_page
 | 
			
		||||
	msr	ttbr1_el1, \tmp
 | 
			
		||||
	isb
 | 
			
		||||
	tlbi	vmalle1
 | 
			
		||||
	dsb	nsh
 | 
			
		||||
	phys_to_ttbr \tmp, \page_table
 | 
			
		||||
	offset_ttbr1 \tmp
 | 
			
		||||
	offset_ttbr1 \tmp, \tmp2
 | 
			
		||||
	msr	ttbr1_el1, \tmp
 | 
			
		||||
	isb
 | 
			
		||||
.endm
 | 
			
		||||
@ -70,7 +70,7 @@ ENTRY(swsusp_arch_suspend_exit)
 | 
			
		||||
	 * We execute from ttbr0, change ttbr1 to our copied linear map tables
 | 
			
		||||
	 * with a break-before-make via the zero page
 | 
			
		||||
	 */
 | 
			
		||||
	break_before_make_ttbr_switch	x5, x0, x6
 | 
			
		||||
	break_before_make_ttbr_switch	x5, x0, x6, x8
 | 
			
		||||
 | 
			
		||||
	mov	x21, x1
 | 
			
		||||
	mov	x30, x2
 | 
			
		||||
@ -101,7 +101,7 @@ ENTRY(swsusp_arch_suspend_exit)
 | 
			
		||||
	dsb	ish		/* wait for PoU cleaning to finish */
 | 
			
		||||
 | 
			
		||||
	/* switch to the restored kernels page tables */
 | 
			
		||||
	break_before_make_ttbr_switch	x25, x21, x6
 | 
			
		||||
	break_before_make_ttbr_switch	x25, x21, x6, x8
 | 
			
		||||
 | 
			
		||||
	ic	ialluis
 | 
			
		||||
	dsb	ish
 | 
			
		||||
 | 
			
		||||
@ -496,7 +496,7 @@ int swsusp_arch_resume(void)
 | 
			
		||||
		rc = -ENOMEM;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
	rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0);
 | 
			
		||||
	rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, PAGE_END);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -62,9 +62,6 @@ out:
 | 
			
		||||
	return default_cmdline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size,
 | 
			
		||||
				       pgprot_t prot);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This routine will be executed with the kernel mapped at its default virtual
 | 
			
		||||
 * address, and if it returns successfully, the kernel will be remapped, and
 | 
			
		||||
@ -93,7 +90,7 @@ u64 __init kaslr_early_init(u64 dt_phys)
 | 
			
		||||
	 * attempt at mapping the FDT in setup_machine()
 | 
			
		||||
	 */
 | 
			
		||||
	early_fixmap_init();
 | 
			
		||||
	fdt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);
 | 
			
		||||
	fdt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);
 | 
			
		||||
	if (!fdt)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
@ -116,15 +113,15 @@ u64 __init kaslr_early_init(u64 dt_phys)
 | 
			
		||||
	/*
 | 
			
		||||
	 * OK, so we are proceeding with KASLR enabled. Calculate a suitable
 | 
			
		||||
	 * kernel image offset from the seed. Let's place the kernel in the
 | 
			
		||||
	 * middle half of the VMALLOC area (VA_BITS - 2), and stay clear of
 | 
			
		||||
	 * middle half of the VMALLOC area (VA_BITS_MIN - 2), and stay clear of
 | 
			
		||||
	 * the lower and upper quarters to avoid colliding with other
 | 
			
		||||
	 * allocations.
 | 
			
		||||
	 * Even if we could randomize at page granularity for 16k and 64k pages,
 | 
			
		||||
	 * let's always round to 2 MB so we don't interfere with the ability to
 | 
			
		||||
	 * map using contiguous PTEs
 | 
			
		||||
	 */
 | 
			
		||||
	mask = ((1UL << (VA_BITS - 2)) - 1) & ~(SZ_2M - 1);
 | 
			
		||||
	offset = BIT(VA_BITS - 3) + (seed & mask);
 | 
			
		||||
	mask = ((1UL << (VA_BITS_MIN - 2)) - 1) & ~(SZ_2M - 1);
 | 
			
		||||
	offset = BIT(VA_BITS_MIN - 3) + (seed & mask);
 | 
			
		||||
 | 
			
		||||
	/* use the top 16 bits to randomize the linear region */
 | 
			
		||||
	memstart_offset_seed = seed >> 48;
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,8 @@
 | 
			
		||||
#define FDT_PROP_INITRD_END	"linux,initrd-end"
 | 
			
		||||
#define FDT_PROP_BOOTARGS	"bootargs"
 | 
			
		||||
#define FDT_PROP_KASLR_SEED	"kaslr-seed"
 | 
			
		||||
#define FDT_PROP_RNG_SEED	"rng-seed"
 | 
			
		||||
#define RNG_SEED_SIZE		128
 | 
			
		||||
 | 
			
		||||
const struct kexec_file_ops * const kexec_file_loaders[] = {
 | 
			
		||||
	&kexec_image_ops,
 | 
			
		||||
@ -102,6 +104,19 @@ static int setup_dtb(struct kimage *image,
 | 
			
		||||
				FDT_PROP_KASLR_SEED);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* add rng-seed */
 | 
			
		||||
	if (rng_is_initialized()) {
 | 
			
		||||
		u8 rng_seed[RNG_SEED_SIZE];
 | 
			
		||||
		get_random_bytes(rng_seed, RNG_SEED_SIZE);
 | 
			
		||||
		ret = fdt_setprop(dtb, off, FDT_PROP_RNG_SEED, rng_seed,
 | 
			
		||||
				RNG_SEED_SIZE);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto out;
 | 
			
		||||
	} else {
 | 
			
		||||
		pr_notice("RNG is not initialised: omitting \"%s\" property\n",
 | 
			
		||||
				FDT_PROP_RNG_SEED);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL;
 | 
			
		||||
@ -110,7 +125,8 @@ out:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * More space needed so that we can add initrd, bootargs and kaslr-seed.
 | 
			
		||||
 * More space needed so that we can add initrd, bootargs, kaslr-seed, and
 | 
			
		||||
 * rng-seed.
 | 
			
		||||
 */
 | 
			
		||||
#define DTB_EXTRA_SPACE 0x1000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/perf/arm_pmu.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
 | 
			
		||||
/* ARMv8 Cortex-A53 specific event types. */
 | 
			
		||||
#define ARMV8_A53_PERFCTR_PREF_LINEFILL				0xC2
 | 
			
		||||
@ -157,7 +158,6 @@ armv8pmu_events_sysfs_show(struct device *dev,
 | 
			
		||||
	return sprintf(page, "event=0x%03llx\n", pmu_attr->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ARMV8_EVENT_ATTR_RESOLVE(m) #m
 | 
			
		||||
#define ARMV8_EVENT_ATTR(name, config) \
 | 
			
		||||
	PMU_EVENT_ATTR(name, armv8_event_attr_##name, \
 | 
			
		||||
		       config, armv8pmu_events_sysfs_show)
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/sysctl.h>
 | 
			
		||||
#include <linux/unistd.h>
 | 
			
		||||
#include <linux/user.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
@ -38,6 +39,7 @@
 | 
			
		||||
#include <trace/events/power.h>
 | 
			
		||||
#include <linux/percpu.h>
 | 
			
		||||
#include <linux/thread_info.h>
 | 
			
		||||
#include <linux/prctl.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/alternative.h>
 | 
			
		||||
#include <asm/arch_gicv3.h>
 | 
			
		||||
@ -307,11 +309,18 @@ static void tls_thread_flush(void)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void flush_tagged_addr_state(void)
 | 
			
		||||
{
 | 
			
		||||
	if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI))
 | 
			
		||||
		clear_thread_flag(TIF_TAGGED_ADDR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_thread(void)
 | 
			
		||||
{
 | 
			
		||||
	fpsimd_flush_thread();
 | 
			
		||||
	tls_thread_flush();
 | 
			
		||||
	flush_ptrace_hw_breakpoint(current);
 | 
			
		||||
	flush_tagged_addr_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void release_thread(struct task_struct *dead_task)
 | 
			
		||||
@ -565,3 +574,70 @@ void arch_setup_new_exec(void)
 | 
			
		||||
 | 
			
		||||
	ptrauth_thread_init_user(current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
 | 
			
		||||
/*
 | 
			
		||||
 * Control the relaxed ABI allowing tagged user addresses into the kernel.
 | 
			
		||||
 */
 | 
			
		||||
static unsigned int tagged_addr_disabled;
 | 
			
		||||
 | 
			
		||||
long set_tagged_addr_ctrl(unsigned long arg)
 | 
			
		||||
{
 | 
			
		||||
	if (is_compat_task())
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (arg & ~PR_TAGGED_ADDR_ENABLE)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Do not allow the enabling of the tagged address ABI if globally
 | 
			
		||||
	 * disabled via sysctl abi.tagged_addr_disabled.
 | 
			
		||||
	 */
 | 
			
		||||
	if (arg & PR_TAGGED_ADDR_ENABLE && tagged_addr_disabled)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	update_thread_flag(TIF_TAGGED_ADDR, arg & PR_TAGGED_ADDR_ENABLE);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
long get_tagged_addr_ctrl(void)
 | 
			
		||||
{
 | 
			
		||||
	if (is_compat_task())
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (test_thread_flag(TIF_TAGGED_ADDR))
 | 
			
		||||
		return PR_TAGGED_ADDR_ENABLE;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Global sysctl to disable the tagged user addresses support. This control
 | 
			
		||||
 * only prevents the tagged address ABI enabling via prctl() and does not
 | 
			
		||||
 * disable it for tasks that already opted in to the relaxed ABI.
 | 
			
		||||
 */
 | 
			
		||||
static int zero;
 | 
			
		||||
static int one = 1;
 | 
			
		||||
 | 
			
		||||
static struct ctl_table tagged_addr_sysctl_table[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.procname	= "tagged_addr_disabled",
 | 
			
		||||
		.mode		= 0644,
 | 
			
		||||
		.data		= &tagged_addr_disabled,
 | 
			
		||||
		.maxlen		= sizeof(int),
 | 
			
		||||
		.proc_handler	= proc_dointvec_minmax,
 | 
			
		||||
		.extra1		= &zero,
 | 
			
		||||
		.extra2		= &one,
 | 
			
		||||
	},
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init tagged_addr_init(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!register_sysctl("abi", tagged_addr_sysctl_table))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
core_initcall(tagged_addr_init);
 | 
			
		||||
#endif	/* CONFIG_ARM64_TAGGED_ADDR_ABI */
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,11 @@ static int cpu_psci_cpu_boot(unsigned int cpu)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
static bool cpu_psci_cpu_can_disable(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return !psci_tos_resident_on(cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cpu_psci_cpu_disable(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	/* Fail early if we don't have CPU_OFF support */
 | 
			
		||||
@ -105,14 +110,11 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
 | 
			
		||||
 | 
			
		||||
const struct cpu_operations cpu_psci_ops = {
 | 
			
		||||
	.name		= "psci",
 | 
			
		||||
#ifdef CONFIG_CPU_IDLE
 | 
			
		||||
	.cpu_init_idle	= psci_cpu_init_idle,
 | 
			
		||||
	.cpu_suspend	= psci_cpu_suspend_enter,
 | 
			
		||||
#endif
 | 
			
		||||
	.cpu_init	= cpu_psci_cpu_init,
 | 
			
		||||
	.cpu_prepare	= cpu_psci_cpu_prepare,
 | 
			
		||||
	.cpu_boot	= cpu_psci_cpu_boot,
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
	.cpu_can_disable = cpu_psci_cpu_can_disable,
 | 
			
		||||
	.cpu_disable	= cpu_psci_cpu_disable,
 | 
			
		||||
	.cpu_die	= cpu_psci_cpu_die,
 | 
			
		||||
	.cpu_kill	= cpu_psci_cpu_kill,
 | 
			
		||||
 | 
			
		||||
@ -170,9 +170,13 @@ static void __init smp_build_mpidr_hash(void)
 | 
			
		||||
 | 
			
		||||
static void __init setup_machine_fdt(phys_addr_t dt_phys)
 | 
			
		||||
{
 | 
			
		||||
	void *dt_virt = fixmap_remap_fdt(dt_phys);
 | 
			
		||||
	int size;
 | 
			
		||||
	void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);
 | 
			
		||||
	const char *name;
 | 
			
		||||
 | 
			
		||||
	if (dt_virt)
 | 
			
		||||
		memblock_reserve(dt_phys, size);
 | 
			
		||||
 | 
			
		||||
	if (!dt_virt || !early_init_dt_scan(dt_virt)) {
 | 
			
		||||
		pr_crit("\n"
 | 
			
		||||
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
 | 
			
		||||
@ -184,6 +188,9 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys)
 | 
			
		||||
			cpu_relax();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Early fixups are done, map the FDT as read-only now */
 | 
			
		||||
	fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
 | 
			
		||||
 | 
			
		||||
	name = of_flat_dt_get_machine_name();
 | 
			
		||||
	if (!name)
 | 
			
		||||
		return;
 | 
			
		||||
@ -357,6 +364,15 @@ void __init setup_arch(char **cmdline_p)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool cpu_can_disable(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
	if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_can_disable)
 | 
			
		||||
		return cpu_ops[cpu]->cpu_can_disable(cpu);
 | 
			
		||||
#endif
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init topology_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
@ -366,7 +382,7 @@ static int __init topology_init(void)
 | 
			
		||||
 | 
			
		||||
	for_each_possible_cpu(i) {
 | 
			
		||||
		struct cpu *cpu = &per_cpu(cpu_data.cpu, i);
 | 
			
		||||
		cpu->hotpluggable = 1;
 | 
			
		||||
		cpu->hotpluggable = cpu_can_disable(i);
 | 
			
		||||
		register_cpu(cpu, i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -123,7 +123,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
 | 
			
		||||
		 * time out.
 | 
			
		||||
		 */
 | 
			
		||||
		wait_for_completion_timeout(&cpu_running,
 | 
			
		||||
					    msecs_to_jiffies(1000));
 | 
			
		||||
					    msecs_to_jiffies(5000));
 | 
			
		||||
 | 
			
		||||
		if (!cpu_online(cpu)) {
 | 
			
		||||
			pr_crit("CPU%u: failed to come online\n", cpu);
 | 
			
		||||
@ -136,6 +136,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
 | 
			
		||||
 | 
			
		||||
	secondary_data.task = NULL;
 | 
			
		||||
	secondary_data.stack = NULL;
 | 
			
		||||
	__flush_dcache_area(&secondary_data, sizeof(secondary_data));
 | 
			
		||||
	status = READ_ONCE(secondary_data.status);
 | 
			
		||||
	if (ret && status) {
 | 
			
		||||
 | 
			
		||||
@ -146,6 +147,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
 | 
			
		||||
		default:
 | 
			
		||||
			pr_err("CPU%u: failed in unknown state : 0x%lx\n",
 | 
			
		||||
					cpu, status);
 | 
			
		||||
			cpus_stuck_in_kernel++;
 | 
			
		||||
			break;
 | 
			
		||||
		case CPU_KILL_ME:
 | 
			
		||||
			if (!op_cpu_kill(cpu)) {
 | 
			
		||||
 | 
			
		||||
@ -14,250 +14,13 @@
 | 
			
		||||
#include <linux/acpi.h>
 | 
			
		||||
#include <linux/arch_topology.h>
 | 
			
		||||
#include <linux/cacheinfo.h>
 | 
			
		||||
#include <linux/cpu.h>
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/percpu.h>
 | 
			
		||||
#include <linux/node.h>
 | 
			
		||||
#include <linux/nodemask.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/sched/topology.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/cpu.h>
 | 
			
		||||
#include <asm/cputype.h>
 | 
			
		||||
#include <asm/topology.h>
 | 
			
		||||
 | 
			
		||||
static int __init get_cpu_for_node(struct device_node *node)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu_node;
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	cpu_node = of_parse_phandle(node, "cpu", 0);
 | 
			
		||||
	if (!cpu_node)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	cpu = of_cpu_node_to_id(cpu_node);
 | 
			
		||||
	if (cpu >= 0)
 | 
			
		||||
		topology_parse_cpu_capacity(cpu_node, cpu);
 | 
			
		||||
	else
 | 
			
		||||
		pr_crit("Unable to find CPU node for %pOF\n", cpu_node);
 | 
			
		||||
 | 
			
		||||
	of_node_put(cpu_node);
 | 
			
		||||
	return cpu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_core(struct device_node *core, int package_id,
 | 
			
		||||
			     int core_id)
 | 
			
		||||
{
 | 
			
		||||
	char name[10];
 | 
			
		||||
	bool leaf = true;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	int cpu;
 | 
			
		||||
	struct device_node *t;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "thread%d", i);
 | 
			
		||||
		t = of_get_child_by_name(core, name);
 | 
			
		||||
		if (t) {
 | 
			
		||||
			leaf = false;
 | 
			
		||||
			cpu = get_cpu_for_node(t);
 | 
			
		||||
			if (cpu >= 0) {
 | 
			
		||||
				cpu_topology[cpu].package_id = package_id;
 | 
			
		||||
				cpu_topology[cpu].core_id = core_id;
 | 
			
		||||
				cpu_topology[cpu].thread_id = i;
 | 
			
		||||
			} else {
 | 
			
		||||
				pr_err("%pOF: Can't get CPU for thread\n",
 | 
			
		||||
				       t);
 | 
			
		||||
				of_node_put(t);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			of_node_put(t);
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (t);
 | 
			
		||||
 | 
			
		||||
	cpu = get_cpu_for_node(core);
 | 
			
		||||
	if (cpu >= 0) {
 | 
			
		||||
		if (!leaf) {
 | 
			
		||||
			pr_err("%pOF: Core has both threads and CPU\n",
 | 
			
		||||
			       core);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cpu_topology[cpu].package_id = package_id;
 | 
			
		||||
		cpu_topology[cpu].core_id = core_id;
 | 
			
		||||
	} else if (leaf) {
 | 
			
		||||
		pr_err("%pOF: Can't get CPU for leaf core\n", core);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_cluster(struct device_node *cluster, int depth)
 | 
			
		||||
{
 | 
			
		||||
	char name[10];
 | 
			
		||||
	bool leaf = true;
 | 
			
		||||
	bool has_cores = false;
 | 
			
		||||
	struct device_node *c;
 | 
			
		||||
	static int package_id __initdata;
 | 
			
		||||
	int core_id = 0;
 | 
			
		||||
	int i, ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * First check for child clusters; we currently ignore any
 | 
			
		||||
	 * information about the nesting of clusters and present the
 | 
			
		||||
	 * scheduler with a flat list of them.
 | 
			
		||||
	 */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "cluster%d", i);
 | 
			
		||||
		c = of_get_child_by_name(cluster, name);
 | 
			
		||||
		if (c) {
 | 
			
		||||
			leaf = false;
 | 
			
		||||
			ret = parse_cluster(c, depth + 1);
 | 
			
		||||
			of_node_put(c);
 | 
			
		||||
			if (ret != 0)
 | 
			
		||||
				return ret;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (c);
 | 
			
		||||
 | 
			
		||||
	/* Now check for cores */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "core%d", i);
 | 
			
		||||
		c = of_get_child_by_name(cluster, name);
 | 
			
		||||
		if (c) {
 | 
			
		||||
			has_cores = true;
 | 
			
		||||
 | 
			
		||||
			if (depth == 0) {
 | 
			
		||||
				pr_err("%pOF: cpu-map children should be clusters\n",
 | 
			
		||||
				       c);
 | 
			
		||||
				of_node_put(c);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (leaf) {
 | 
			
		||||
				ret = parse_core(c, package_id, core_id++);
 | 
			
		||||
			} else {
 | 
			
		||||
				pr_err("%pOF: Non-leaf cluster with core %s\n",
 | 
			
		||||
				       cluster, name);
 | 
			
		||||
				ret = -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			of_node_put(c);
 | 
			
		||||
			if (ret != 0)
 | 
			
		||||
				return ret;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (c);
 | 
			
		||||
 | 
			
		||||
	if (leaf && !has_cores)
 | 
			
		||||
		pr_warn("%pOF: empty cluster\n", cluster);
 | 
			
		||||
 | 
			
		||||
	if (leaf)
 | 
			
		||||
		package_id++;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_dt_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cn, *map;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	cn = of_find_node_by_path("/cpus");
 | 
			
		||||
	if (!cn) {
 | 
			
		||||
		pr_err("No CPU information found in DT\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * When topology is provided cpu-map is essentially a root
 | 
			
		||||
	 * cluster with restricted subnodes.
 | 
			
		||||
	 */
 | 
			
		||||
	map = of_get_child_by_name(cn, "cpu-map");
 | 
			
		||||
	if (!map)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	ret = parse_cluster(map, 0);
 | 
			
		||||
	if (ret != 0)
 | 
			
		||||
		goto out_map;
 | 
			
		||||
 | 
			
		||||
	topology_normalize_cpu_scale();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check that all cores are in the topology; the SMP code will
 | 
			
		||||
	 * only mark cores described in the DT as possible.
 | 
			
		||||
	 */
 | 
			
		||||
	for_each_possible_cpu(cpu)
 | 
			
		||||
		if (cpu_topology[cpu].package_id == -1)
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
out_map:
 | 
			
		||||
	of_node_put(map);
 | 
			
		||||
out:
 | 
			
		||||
	of_node_put(cn);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * cpu topology table
 | 
			
		||||
 */
 | 
			
		||||
struct cpu_topology cpu_topology[NR_CPUS];
 | 
			
		||||
EXPORT_SYMBOL_GPL(cpu_topology);
 | 
			
		||||
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	const cpumask_t *core_mask = cpumask_of_node(cpu_to_node(cpu));
 | 
			
		||||
 | 
			
		||||
	/* Find the smaller of NUMA, core or LLC siblings */
 | 
			
		||||
	if (cpumask_subset(&cpu_topology[cpu].core_sibling, core_mask)) {
 | 
			
		||||
		/* not numa in package, lets use the package siblings */
 | 
			
		||||
		core_mask = &cpu_topology[cpu].core_sibling;
 | 
			
		||||
	}
 | 
			
		||||
	if (cpu_topology[cpu].llc_id != -1) {
 | 
			
		||||
		if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
 | 
			
		||||
			core_mask = &cpu_topology[cpu].llc_sibling;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return core_mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_siblings_masks(unsigned int cpuid)
 | 
			
		||||
{
 | 
			
		||||
	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	/* update core and thread sibling masks */
 | 
			
		||||
	for_each_online_cpu(cpu) {
 | 
			
		||||
		cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->llc_id == cpu_topo->llc_id) {
 | 
			
		||||
			cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
 | 
			
		||||
			cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->package_id != cpu_topo->package_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
 | 
			
		||||
		cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->core_id != cpu_topo->core_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
 | 
			
		||||
		cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void store_cpu_topology(unsigned int cpuid)
 | 
			
		||||
{
 | 
			
		||||
	struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
@ -296,60 +59,31 @@ topology_populated:
 | 
			
		||||
	update_siblings_masks(cpuid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clear_cpu_topology(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	struct cpu_topology *cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
	cpumask_clear(&cpu_topo->llc_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->llc_sibling);
 | 
			
		||||
 | 
			
		||||
	cpumask_clear(&cpu_topo->core_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
 | 
			
		||||
	cpumask_clear(&cpu_topo->thread_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init reset_cpu_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cpu;
 | 
			
		||||
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		struct cpu_topology *cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
		cpu_topo->thread_id = -1;
 | 
			
		||||
		cpu_topo->core_id = 0;
 | 
			
		||||
		cpu_topo->package_id = -1;
 | 
			
		||||
		cpu_topo->llc_id = -1;
 | 
			
		||||
 | 
			
		||||
		clear_cpu_topology(cpu);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void remove_cpu_topology(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int sibling;
 | 
			
		||||
 | 
			
		||||
	for_each_cpu(sibling, topology_core_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_core_cpumask(sibling));
 | 
			
		||||
	for_each_cpu(sibling, topology_sibling_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
 | 
			
		||||
	for_each_cpu(sibling, topology_llc_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));
 | 
			
		||||
 | 
			
		||||
	clear_cpu_topology(cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ACPI
 | 
			
		||||
static bool __init acpi_cpu_is_threaded(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int is_threaded = acpi_pptt_cpu_is_thread(cpu);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * if the PPTT doesn't have thread information, assume a homogeneous
 | 
			
		||||
	 * machine and return the current CPU's thread state.
 | 
			
		||||
	 */
 | 
			
		||||
	if (is_threaded < 0)
 | 
			
		||||
		is_threaded = read_cpuid_mpidr() & MPIDR_MT_BITMASK;
 | 
			
		||||
 | 
			
		||||
	return !!is_threaded;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Propagate the topology information of the processor_topology_node tree to the
 | 
			
		||||
 * cpu_topology array.
 | 
			
		||||
 */
 | 
			
		||||
static int __init parse_acpi_topology(void)
 | 
			
		||||
int __init parse_acpi_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	bool is_threaded;
 | 
			
		||||
	int cpu, topology_id;
 | 
			
		||||
 | 
			
		||||
	is_threaded = read_cpuid_mpidr() & MPIDR_MT_BITMASK;
 | 
			
		||||
	if (acpi_disabled)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		int i, cache_id;
 | 
			
		||||
@ -358,7 +92,7 @@ static int __init parse_acpi_topology(void)
 | 
			
		||||
		if (topology_id < 0)
 | 
			
		||||
			return topology_id;
 | 
			
		||||
 | 
			
		||||
		if (is_threaded) {
 | 
			
		||||
		if (acpi_cpu_is_threaded(cpu)) {
 | 
			
		||||
			cpu_topology[cpu].thread_id = topology_id;
 | 
			
		||||
			topology_id = find_acpi_cpu_topology(cpu, 1);
 | 
			
		||||
			cpu_topology[cpu].core_id   = topology_id;
 | 
			
		||||
@ -384,24 +118,6 @@ static int __init parse_acpi_topology(void)
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
static inline int __init parse_acpi_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void __init init_cpu_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	reset_cpu_topology();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Discard anything that was parsed if we hit an error so we
 | 
			
		||||
	 * don't use partial information.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!acpi_disabled && parse_acpi_topology())
 | 
			
		||||
		reset_cpu_topology();
 | 
			
		||||
	else if (of_have_populated_dt() && parse_dt_topology())
 | 
			
		||||
		reset_cpu_topology();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -264,7 +264,7 @@ static bool __hyp_text __translate_far_to_hpfar(u64 far, u64 *hpfar)
 | 
			
		||||
	tmp = read_sysreg(par_el1);
 | 
			
		||||
	write_sysreg(par, par_el1);
 | 
			
		||||
 | 
			
		||||
	if (unlikely(tmp & 1))
 | 
			
		||||
	if (unlikely(tmp & SYS_PAR_EL1_F))
 | 
			
		||||
		return false; /* Translation failed, back to guest */
 | 
			
		||||
 | 
			
		||||
	/* Convert PAR to HPFAR format */
 | 
			
		||||
 | 
			
		||||
@ -29,25 +29,25 @@ static void compute_layout(void)
 | 
			
		||||
	int kva_msb;
 | 
			
		||||
 | 
			
		||||
	/* Where is my RAM region? */
 | 
			
		||||
	hyp_va_msb  = idmap_addr & BIT(VA_BITS - 1);
 | 
			
		||||
	hyp_va_msb ^= BIT(VA_BITS - 1);
 | 
			
		||||
	hyp_va_msb  = idmap_addr & BIT(vabits_actual - 1);
 | 
			
		||||
	hyp_va_msb ^= BIT(vabits_actual - 1);
 | 
			
		||||
 | 
			
		||||
	kva_msb = fls64((u64)phys_to_virt(memblock_start_of_DRAM()) ^
 | 
			
		||||
			(u64)(high_memory - 1));
 | 
			
		||||
 | 
			
		||||
	if (kva_msb == (VA_BITS - 1)) {
 | 
			
		||||
	if (kva_msb == (vabits_actual - 1)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * No space in the address, let's compute the mask so
 | 
			
		||||
		 * that it covers (VA_BITS - 1) bits, and the region
 | 
			
		||||
		 * that it covers (vabits_actual - 1) bits, and the region
 | 
			
		||||
		 * bit. The tag stays set to zero.
 | 
			
		||||
		 */
 | 
			
		||||
		va_mask  = BIT(VA_BITS - 1) - 1;
 | 
			
		||||
		va_mask  = BIT(vabits_actual - 1) - 1;
 | 
			
		||||
		va_mask |= hyp_va_msb;
 | 
			
		||||
	} else {
 | 
			
		||||
		/*
 | 
			
		||||
		 * We do have some free bits to insert a random tag.
 | 
			
		||||
		 * Hyp VAs are now created from kernel linear map VAs
 | 
			
		||||
		 * using the following formula (with V == VA_BITS):
 | 
			
		||||
		 * using the following formula (with V == vabits_actual):
 | 
			
		||||
		 *
 | 
			
		||||
		 *  63 ... V |     V-1    | V-2 .. tag_lsb | tag_lsb - 1 .. 0
 | 
			
		||||
		 *  ---------------------------------------------------------
 | 
			
		||||
@ -55,7 +55,7 @@ static void compute_layout(void)
 | 
			
		||||
		 */
 | 
			
		||||
		tag_lsb = kva_msb;
 | 
			
		||||
		va_mask = GENMASK_ULL(tag_lsb - 1, 0);
 | 
			
		||||
		tag_val = get_random_long() & GENMASK_ULL(VA_BITS - 2, tag_lsb);
 | 
			
		||||
		tag_val = get_random_long() & GENMASK_ULL(vabits_actual - 2, tag_lsb);
 | 
			
		||||
		tag_val |= hyp_va_msb;
 | 
			
		||||
		tag_val >>= tag_lsb;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -33,3 +33,5 @@ UBSAN_SANITIZE_atomic_ll_sc.o	:= n
 | 
			
		||||
lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_CRC32) += crc32.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								arch/arm64/lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								arch/arm64/lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
#include <linux/error-injection.h>
 | 
			
		||||
#include <linux/kprobes.h>
 | 
			
		||||
 | 
			
		||||
void override_function_with_return(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * 'regs' represents the state on entry of a predefined function in
 | 
			
		||||
	 * the kernel/module and which is captured on a kprobe.
 | 
			
		||||
	 *
 | 
			
		||||
	 * When kprobe returns back from exception it will override the end
 | 
			
		||||
	 * of probed function and directly return to the predefined
 | 
			
		||||
	 * function's caller.
 | 
			
		||||
	 */
 | 
			
		||||
	instruction_pointer_set(regs, procedure_link_pointer(regs));
 | 
			
		||||
}
 | 
			
		||||
NOKPROBE_SYMBOL(override_function_with_return);
 | 
			
		||||
@ -25,9 +25,20 @@
 | 
			
		||||
#include <asm/pgtable-hwdef.h>
 | 
			
		||||
#include <asm/ptdump.h>
 | 
			
		||||
 | 
			
		||||
static const struct addr_marker address_markers[] = {
 | 
			
		||||
 | 
			
		||||
enum address_markers_idx {
 | 
			
		||||
	PAGE_OFFSET_NR = 0,
 | 
			
		||||
	PAGE_END_NR,
 | 
			
		||||
#ifdef CONFIG_KASAN
 | 
			
		||||
	{ KASAN_SHADOW_START,		"Kasan shadow start" },
 | 
			
		||||
	KASAN_START_NR,
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct addr_marker address_markers[] = {
 | 
			
		||||
	{ PAGE_OFFSET,			"Linear Mapping start" },
 | 
			
		||||
	{ 0 /* PAGE_END */,		"Linear Mapping end" },
 | 
			
		||||
#ifdef CONFIG_KASAN
 | 
			
		||||
	{ 0 /* KASAN_SHADOW_START */,	"Kasan shadow start" },
 | 
			
		||||
	{ KASAN_SHADOW_END,		"Kasan shadow end" },
 | 
			
		||||
#endif
 | 
			
		||||
	{ MODULES_VADDR,		"Modules start" },
 | 
			
		||||
@ -42,7 +53,6 @@ static const struct addr_marker address_markers[] = {
 | 
			
		||||
	{ VMEMMAP_START,		"vmemmap start" },
 | 
			
		||||
	{ VMEMMAP_START + VMEMMAP_SIZE,	"vmemmap end" },
 | 
			
		||||
#endif
 | 
			
		||||
	{ PAGE_OFFSET,			"Linear mapping" },
 | 
			
		||||
	{ -1,				NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -376,7 +386,7 @@ static void ptdump_initialize(void)
 | 
			
		||||
static struct ptdump_info kernel_ptdump_info = {
 | 
			
		||||
	.mm		= &init_mm,
 | 
			
		||||
	.markers	= address_markers,
 | 
			
		||||
	.base_addr	= VA_START,
 | 
			
		||||
	.base_addr	= PAGE_OFFSET,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void ptdump_check_wx(void)
 | 
			
		||||
@ -390,7 +400,7 @@ void ptdump_check_wx(void)
 | 
			
		||||
		.check_wx = true,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	walk_pgd(&st, &init_mm, VA_START);
 | 
			
		||||
	walk_pgd(&st, &init_mm, PAGE_OFFSET);
 | 
			
		||||
	note_page(&st, 0, 0, 0);
 | 
			
		||||
	if (st.wx_pages || st.uxn_pages)
 | 
			
		||||
		pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
 | 
			
		||||
@ -401,6 +411,10 @@ void ptdump_check_wx(void)
 | 
			
		||||
 | 
			
		||||
static int ptdump_init(void)
 | 
			
		||||
{
 | 
			
		||||
	address_markers[PAGE_END_NR].start_address = PAGE_END;
 | 
			
		||||
#ifdef CONFIG_KASAN
 | 
			
		||||
	address_markers[KASAN_START_NR].start_address = KASAN_SHADOW_START;
 | 
			
		||||
#endif
 | 
			
		||||
	ptdump_initialize();
 | 
			
		||||
	ptdump_debugfs_register(&kernel_ptdump_info, "kernel_page_tables");
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/acpi.h>
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/extable.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
@ -109,7 +110,7 @@ static inline bool is_ttbr0_addr(unsigned long addr)
 | 
			
		||||
static inline bool is_ttbr1_addr(unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	/* TTBR1 addresses may have a tag if KASAN_SW_TAGS is in use */
 | 
			
		||||
	return arch_kasan_reset_tag(addr) >= VA_START;
 | 
			
		||||
	return arch_kasan_reset_tag(addr) >= PAGE_OFFSET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -138,10 +139,9 @@ static void show_pte(unsigned long addr)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_alert("%s pgtable: %luk pages, %u-bit VAs, pgdp=%016lx\n",
 | 
			
		||||
	pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n",
 | 
			
		||||
		 mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K,
 | 
			
		||||
		 mm == &init_mm ? VA_BITS : (int)vabits_user,
 | 
			
		||||
		 (unsigned long)virt_to_phys(mm->pgd));
 | 
			
		||||
		 vabits_actual, (unsigned long)virt_to_phys(mm->pgd));
 | 
			
		||||
	pgdp = pgd_offset(mm, addr);
 | 
			
		||||
	pgd = READ_ONCE(*pgdp);
 | 
			
		||||
	pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd));
 | 
			
		||||
@ -242,6 +242,34 @@ static inline bool is_el1_permission_fault(unsigned long addr, unsigned int esr,
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr,
 | 
			
		||||
							unsigned int esr,
 | 
			
		||||
							struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u64 par, dfsc;
 | 
			
		||||
 | 
			
		||||
	if (ESR_ELx_EC(esr) != ESR_ELx_EC_DABT_CUR ||
 | 
			
		||||
	    (esr & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	asm volatile("at s1e1r, %0" :: "r" (addr));
 | 
			
		||||
	isb();
 | 
			
		||||
	par = read_sysreg(par_el1);
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
 | 
			
		||||
	if (!(par & SYS_PAR_EL1_F))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If we got a different type of fault from the AT instruction,
 | 
			
		||||
	 * treat the translation fault as spurious.
 | 
			
		||||
	 */
 | 
			
		||||
	dfsc = FIELD_PREP(SYS_PAR_EL1_FST, par);
 | 
			
		||||
	return (dfsc & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void die_kernel_fault(const char *msg, unsigned long addr,
 | 
			
		||||
			     unsigned int esr, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
@ -270,6 +298,10 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 | 
			
		||||
	if (!is_el1_instruction_abort(esr) && fixup_exception(regs))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (WARN_RATELIMIT(is_spurious_el1_translation_fault(addr, esr, regs),
 | 
			
		||||
	    "Ignoring spurious kernel translation fault at virtual address %016lx\n", addr))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (is_el1_permission_fault(addr, esr, regs)) {
 | 
			
		||||
		if (esr & ESR_ELx_WNR)
 | 
			
		||||
			msg = "write to read-only memory";
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,12 @@
 | 
			
		||||
s64 memstart_addr __ro_after_init = -1;
 | 
			
		||||
EXPORT_SYMBOL(memstart_addr);
 | 
			
		||||
 | 
			
		||||
s64 physvirt_offset __ro_after_init;
 | 
			
		||||
EXPORT_SYMBOL(physvirt_offset);
 | 
			
		||||
 | 
			
		||||
struct page *vmemmap __ro_after_init;
 | 
			
		||||
EXPORT_SYMBOL(vmemmap);
 | 
			
		||||
 | 
			
		||||
phys_addr_t arm64_dma_phys_limit __ro_after_init;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_KEXEC_CORE
 | 
			
		||||
@ -301,7 +307,7 @@ static void __init fdt_enforce_memory_region(void)
 | 
			
		||||
 | 
			
		||||
void __init arm64_memblock_init(void)
 | 
			
		||||
{
 | 
			
		||||
	const s64 linear_region_size = -(s64)PAGE_OFFSET;
 | 
			
		||||
	const s64 linear_region_size = BIT(vabits_actual - 1);
 | 
			
		||||
 | 
			
		||||
	/* Handle linux,usable-memory-range property */
 | 
			
		||||
	fdt_enforce_memory_region();
 | 
			
		||||
@ -309,19 +315,26 @@ void __init arm64_memblock_init(void)
 | 
			
		||||
	/* Remove memory above our supported physical address size */
 | 
			
		||||
	memblock_remove(1ULL << PHYS_MASK_SHIFT, ULLONG_MAX);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Ensure that the linear region takes up exactly half of the kernel
 | 
			
		||||
	 * virtual address space. This way, we can distinguish a linear address
 | 
			
		||||
	 * from a kernel/module/vmalloc address by testing a single bit.
 | 
			
		||||
	 */
 | 
			
		||||
	BUILD_BUG_ON(linear_region_size != BIT(VA_BITS - 1));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Select a suitable value for the base of physical memory.
 | 
			
		||||
	 */
 | 
			
		||||
	memstart_addr = round_down(memblock_start_of_DRAM(),
 | 
			
		||||
				   ARM64_MEMSTART_ALIGN);
 | 
			
		||||
 | 
			
		||||
	physvirt_offset = PHYS_OFFSET - PAGE_OFFSET;
 | 
			
		||||
 | 
			
		||||
	vmemmap = ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If we are running with a 52-bit kernel VA config on a system that
 | 
			
		||||
	 * does not support it, we have to offset our vmemmap and physvirt_offset
 | 
			
		||||
	 * s.t. we avoid the 52-bit portion of the direct linear map
 | 
			
		||||
	 */
 | 
			
		||||
	if (IS_ENABLED(CONFIG_ARM64_VA_BITS_52) && (vabits_actual != 52)) {
 | 
			
		||||
		vmemmap += (_PAGE_OFFSET(48) - _PAGE_OFFSET(52)) >> PAGE_SHIFT;
 | 
			
		||||
		physvirt_offset = PHYS_OFFSET - _PAGE_OFFSET(48);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Remove the memory that we will not be able to cover with the
 | 
			
		||||
	 * linear mapping. Take care not to clip the kernel which may be
 | 
			
		||||
 | 
			
		||||
@ -156,7 +156,8 @@ asmlinkage void __init kasan_early_init(void)
 | 
			
		||||
{
 | 
			
		||||
	BUILD_BUG_ON(KASAN_SHADOW_OFFSET !=
 | 
			
		||||
		KASAN_SHADOW_END - (1UL << (64 - KASAN_SHADOW_SCALE_SHIFT)));
 | 
			
		||||
	BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, PGDIR_SIZE));
 | 
			
		||||
	BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS), PGDIR_SIZE));
 | 
			
		||||
	BUILD_BUG_ON(!IS_ALIGNED(_KASAN_SHADOW_START(VA_BITS_MIN), PGDIR_SIZE));
 | 
			
		||||
	BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, PGDIR_SIZE));
 | 
			
		||||
	kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE,
 | 
			
		||||
			   true);
 | 
			
		||||
@ -225,10 +226,10 @@ void __init kasan_init(void)
 | 
			
		||||
	kasan_map_populate(kimg_shadow_start, kimg_shadow_end,
 | 
			
		||||
			   early_pfn_to_nid(virt_to_pfn(lm_alias(_text))));
 | 
			
		||||
 | 
			
		||||
	kasan_populate_early_shadow((void *)KASAN_SHADOW_START,
 | 
			
		||||
				    (void *)mod_shadow_start);
 | 
			
		||||
	kasan_populate_early_shadow(kasan_mem_to_shadow((void *)PAGE_END),
 | 
			
		||||
				   (void *)mod_shadow_start);
 | 
			
		||||
	kasan_populate_early_shadow((void *)kimg_shadow_end,
 | 
			
		||||
				    kasan_mem_to_shadow((void *)PAGE_OFFSET));
 | 
			
		||||
				   (void *)KASAN_SHADOW_END);
 | 
			
		||||
 | 
			
		||||
	if (kimg_shadow_start > mod_shadow_end)
 | 
			
		||||
		kasan_populate_early_shadow((void *)mod_shadow_end,
 | 
			
		||||
 | 
			
		||||
@ -40,8 +40,9 @@
 | 
			
		||||
 | 
			
		||||
u64 idmap_t0sz = TCR_T0SZ(VA_BITS);
 | 
			
		||||
u64 idmap_ptrs_per_pgd = PTRS_PER_PGD;
 | 
			
		||||
u64 vabits_user __ro_after_init;
 | 
			
		||||
EXPORT_SYMBOL(vabits_user);
 | 
			
		||||
 | 
			
		||||
u64 __section(".mmuoff.data.write") vabits_actual;
 | 
			
		||||
EXPORT_SYMBOL(vabits_actual);
 | 
			
		||||
 | 
			
		||||
u64 kimage_voffset __ro_after_init;
 | 
			
		||||
EXPORT_SYMBOL(kimage_voffset);
 | 
			
		||||
@ -398,7 +399,7 @@ static phys_addr_t pgd_pgtable_alloc(int shift)
 | 
			
		||||
static void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
 | 
			
		||||
				  phys_addr_t size, pgprot_t prot)
 | 
			
		||||
{
 | 
			
		||||
	if (virt < VMALLOC_START) {
 | 
			
		||||
	if ((virt >= PAGE_END) && (virt < VMALLOC_START)) {
 | 
			
		||||
		pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n",
 | 
			
		||||
			&phys, virt);
 | 
			
		||||
		return;
 | 
			
		||||
@ -425,7 +426,7 @@ void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
 | 
			
		||||
static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
 | 
			
		||||
				phys_addr_t size, pgprot_t prot)
 | 
			
		||||
{
 | 
			
		||||
	if (virt < VMALLOC_START) {
 | 
			
		||||
	if ((virt >= PAGE_END) && (virt < VMALLOC_START)) {
 | 
			
		||||
		pr_warn("BUG: not updating mapping for %pa at 0x%016lx - outside kernel range\n",
 | 
			
		||||
			&phys, virt);
 | 
			
		||||
		return;
 | 
			
		||||
@ -646,6 +647,8 @@ static void __init map_kernel(pgd_t *pgdp)
 | 
			
		||||
		set_pgd(pgd_offset_raw(pgdp, FIXADDR_START),
 | 
			
		||||
			READ_ONCE(*pgd_offset_k(FIXADDR_START)));
 | 
			
		||||
	} else if (CONFIG_PGTABLE_LEVELS > 3) {
 | 
			
		||||
		pgd_t *bm_pgdp;
 | 
			
		||||
		pud_t *bm_pudp;
 | 
			
		||||
		/*
 | 
			
		||||
		 * The fixmap shares its top level pgd entry with the kernel
 | 
			
		||||
		 * mapping. This can really only occur when we are running
 | 
			
		||||
@ -653,9 +656,9 @@ static void __init map_kernel(pgd_t *pgdp)
 | 
			
		||||
		 * 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));
 | 
			
		||||
		bm_pgdp = pgd_offset_raw(pgdp, FIXADDR_START);
 | 
			
		||||
		bm_pudp = pud_set_fixmap_offset(bm_pgdp, FIXADDR_START);
 | 
			
		||||
		pud_populate(&init_mm, bm_pudp, lm_alias(bm_pmd));
 | 
			
		||||
		pud_clear_fixmap();
 | 
			
		||||
	} else {
 | 
			
		||||
		BUG();
 | 
			
		||||
@ -876,7 +879,7 @@ void __set_fixmap(enum fixed_addresses idx,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
 | 
			
		||||
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;
 | 
			
		||||
@ -929,19 +932,6 @@ void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
 | 
			
		||||
	return dt_virt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
 | 
			
		||||
{
 | 
			
		||||
	void *dt_virt;
 | 
			
		||||
	int size;
 | 
			
		||||
 | 
			
		||||
	dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
 | 
			
		||||
	if (!dt_virt)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memblock_reserve(dt_phys, size);
 | 
			
		||||
	return dt_virt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int __init arch_ioremap_p4d_supported(void)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
@ -168,7 +168,7 @@ ENDPROC(cpu_do_switch_mm)
 | 
			
		||||
.macro	__idmap_cpu_set_reserved_ttbr1, tmp1, tmp2
 | 
			
		||||
	adrp	\tmp1, empty_zero_page
 | 
			
		||||
	phys_to_ttbr \tmp2, \tmp1
 | 
			
		||||
	offset_ttbr1 \tmp2
 | 
			
		||||
	offset_ttbr1 \tmp2, \tmp1
 | 
			
		||||
	msr	ttbr1_el1, \tmp2
 | 
			
		||||
	isb
 | 
			
		||||
	tlbi	vmalle1
 | 
			
		||||
@ -187,7 +187,7 @@ ENTRY(idmap_cpu_replace_ttbr1)
 | 
			
		||||
 | 
			
		||||
	__idmap_cpu_set_reserved_ttbr1 x1, x3
 | 
			
		||||
 | 
			
		||||
	offset_ttbr1 x0
 | 
			
		||||
	offset_ttbr1 x0, x3
 | 
			
		||||
	msr	ttbr1_el1, x0
 | 
			
		||||
	isb
 | 
			
		||||
 | 
			
		||||
@ -371,7 +371,7 @@ __idmap_kpti_secondary:
 | 
			
		||||
	cbnz	w18, 1b
 | 
			
		||||
 | 
			
		||||
	/* All done, act like nothing happened */
 | 
			
		||||
	offset_ttbr1 swapper_ttb
 | 
			
		||||
	offset_ttbr1 swapper_ttb, x18
 | 
			
		||||
	msr	ttbr1_el1, swapper_ttb
 | 
			
		||||
	isb
 | 
			
		||||
	ret
 | 
			
		||||
@ -447,10 +447,11 @@ ENTRY(__cpu_setup)
 | 
			
		||||
			TCR_TBI0 | TCR_A1 | TCR_KASAN_FLAGS
 | 
			
		||||
	tcr_clear_errata_bits x10, x9, x5
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ARM64_USER_VA_BITS_52
 | 
			
		||||
	ldr_l		x9, vabits_user
 | 
			
		||||
#ifdef CONFIG_ARM64_VA_BITS_52
 | 
			
		||||
	ldr_l		x9, vabits_actual
 | 
			
		||||
	sub		x9, xzr, x9
 | 
			
		||||
	add		x9, x9, #64
 | 
			
		||||
	tcr_set_t1sz	x10, x9
 | 
			
		||||
#else
 | 
			
		||||
	ldr_l		x9, idmap_t0sz
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -1,13 +0,0 @@
 | 
			
		||||
/* SPDX-License-Identifier: GPL-2.0+ */
 | 
			
		||||
 | 
			
		||||
#ifndef _ASM_ERROR_INJECTION_H
 | 
			
		||||
#define _ASM_ERROR_INJECTION_H
 | 
			
		||||
 | 
			
		||||
#include <linux/compiler.h>
 | 
			
		||||
#include <linux/linkage.h>
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm-generic/error-injection.h>
 | 
			
		||||
 | 
			
		||||
void override_function_with_return(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#endif /* _ASM_ERROR_INJECTION_H */
 | 
			
		||||
@ -48,6 +48,7 @@ config RISCV
 | 
			
		||||
	select PCI_MSI if PCI
 | 
			
		||||
	select RISCV_TIMER
 | 
			
		||||
	select GENERIC_IRQ_MULTI_HANDLER
 | 
			
		||||
	select GENERIC_ARCH_TOPOLOGY if SMP
 | 
			
		||||
	select ARCH_HAS_PTE_SPECIAL
 | 
			
		||||
	select ARCH_HAS_MMIOWB
 | 
			
		||||
	select HAVE_EBPF_JIT if 64BIT
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
 * Copyright (C) 2017 SiFive
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/arch_topology.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
@ -35,6 +36,7 @@ static DECLARE_COMPLETION(cpu_running);
 | 
			
		||||
 | 
			
		||||
void __init smp_prepare_boot_cpu(void)
 | 
			
		||||
{
 | 
			
		||||
	init_cpu_topology();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init smp_prepare_cpus(unsigned int max_cpus)
 | 
			
		||||
@ -138,6 +140,7 @@ asmlinkage void __init smp_callin(void)
 | 
			
		||||
 | 
			
		||||
	trap_init();
 | 
			
		||||
	notify_cpu_starting(smp_processor_id());
 | 
			
		||||
	update_siblings_masks(smp_processor_id());
 | 
			
		||||
	set_cpu_online(smp_processor_id(), 1);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Remote TLB flushes are ignored while the CPU is offline, so emit
 | 
			
		||||
 | 
			
		||||
@ -1,13 +0,0 @@
 | 
			
		||||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
			
		||||
#ifndef _ASM_ERROR_INJECTION_H
 | 
			
		||||
#define _ASM_ERROR_INJECTION_H
 | 
			
		||||
 | 
			
		||||
#include <linux/compiler.h>
 | 
			
		||||
#include <linux/linkage.h>
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm-generic/error-injection.h>
 | 
			
		||||
 | 
			
		||||
asmlinkage void just_return_func(void);
 | 
			
		||||
void override_function_with_return(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#endif /* _ASM_ERROR_INJECTION_H */
 | 
			
		||||
@ -540,6 +540,44 @@ static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * check_acpi_cpu_flag() - Determine if CPU node has a flag set
 | 
			
		||||
 * @cpu: Kernel logical CPU number
 | 
			
		||||
 * @rev: The minimum PPTT revision defining the flag
 | 
			
		||||
 * @flag: The flag itself
 | 
			
		||||
 *
 | 
			
		||||
 * Check the node representing a CPU for a given flag.
 | 
			
		||||
 *
 | 
			
		||||
 * Return: -ENOENT if the PPTT doesn't exist, the CPU cannot be found or
 | 
			
		||||
 *	   the table revision isn't new enough.
 | 
			
		||||
 *	   1, any passed flag set
 | 
			
		||||
 *	   0, flag unset
 | 
			
		||||
 */
 | 
			
		||||
static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_table_header *table;
 | 
			
		||||
	acpi_status status;
 | 
			
		||||
	u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
 | 
			
		||||
	struct acpi_pptt_processor *cpu_node = NULL;
 | 
			
		||||
	int ret = -ENOENT;
 | 
			
		||||
 | 
			
		||||
	status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
 | 
			
		||||
	if (ACPI_FAILURE(status)) {
 | 
			
		||||
		acpi_pptt_warn_missing();
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (table->revision >= rev)
 | 
			
		||||
		cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
 | 
			
		||||
 | 
			
		||||
	if (cpu_node)
 | 
			
		||||
		ret = (cpu_node->flags & flag) != 0;
 | 
			
		||||
 | 
			
		||||
	acpi_put_table(table);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * acpi_find_last_cache_level() - Determines the number of cache levels for a PE
 | 
			
		||||
 * @cpu: Kernel logical CPU number
 | 
			
		||||
@ -604,6 +642,20 @@ int cache_setup_acpi(unsigned int cpu)
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * acpi_pptt_cpu_is_thread() - Determine if CPU is a thread
 | 
			
		||||
 * @cpu: Kernel logical CPU number
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 1, a thread
 | 
			
		||||
 *         0, not a thread
 | 
			
		||||
 *         -ENOENT ,if the PPTT doesn't exist, the CPU cannot be found or
 | 
			
		||||
 *         the table revision isn't new enough.
 | 
			
		||||
 */
 | 
			
		||||
int acpi_pptt_cpu_is_thread(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return check_acpi_cpu_flag(cpu, 2, ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * find_acpi_cpu_topology() - Determine a unique topology value for a given CPU
 | 
			
		||||
 * @cpu: Kernel logical CPU number
 | 
			
		||||
@ -664,7 +716,6 @@ int find_acpi_cpu_cache_topology(unsigned int cpu, int level)
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * find_acpi_cpu_topology_package() - Determine a unique CPU package value
 | 
			
		||||
 * @cpu: Kernel logical CPU number
 | 
			
		||||
 | 
			
		||||
@ -202,7 +202,7 @@ config GENERIC_ARCH_TOPOLOGY
 | 
			
		||||
	help
 | 
			
		||||
	  Enable support for architectures common topology code: e.g., parsing
 | 
			
		||||
	  CPU capacity information from DT, usage of such information for
 | 
			
		||||
	  appropriate scaling, sysfs interface for changing capacity values at
 | 
			
		||||
	  appropriate scaling, sysfs interface for reading capacity values at
 | 
			
		||||
	  runtime.
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,11 @@
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/sched/topology.h>
 | 
			
		||||
#include <linux/cpuset.h>
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/percpu.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
 | 
			
		||||
DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
 | 
			
		||||
 | 
			
		||||
@ -241,3 +246,296 @@ static void parsing_done_workfn(struct work_struct *work)
 | 
			
		||||
#else
 | 
			
		||||
core_initcall(free_raw_capacity);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
 | 
			
		||||
static int __init get_cpu_for_node(struct device_node *node)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu_node;
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	cpu_node = of_parse_phandle(node, "cpu", 0);
 | 
			
		||||
	if (!cpu_node)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	cpu = of_cpu_node_to_id(cpu_node);
 | 
			
		||||
	if (cpu >= 0)
 | 
			
		||||
		topology_parse_cpu_capacity(cpu_node, cpu);
 | 
			
		||||
	else
 | 
			
		||||
		pr_crit("Unable to find CPU node for %pOF\n", cpu_node);
 | 
			
		||||
 | 
			
		||||
	of_node_put(cpu_node);
 | 
			
		||||
	return cpu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_core(struct device_node *core, int package_id,
 | 
			
		||||
			     int core_id)
 | 
			
		||||
{
 | 
			
		||||
	char name[10];
 | 
			
		||||
	bool leaf = true;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	int cpu;
 | 
			
		||||
	struct device_node *t;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "thread%d", i);
 | 
			
		||||
		t = of_get_child_by_name(core, name);
 | 
			
		||||
		if (t) {
 | 
			
		||||
			leaf = false;
 | 
			
		||||
			cpu = get_cpu_for_node(t);
 | 
			
		||||
			if (cpu >= 0) {
 | 
			
		||||
				cpu_topology[cpu].package_id = package_id;
 | 
			
		||||
				cpu_topology[cpu].core_id = core_id;
 | 
			
		||||
				cpu_topology[cpu].thread_id = i;
 | 
			
		||||
			} else {
 | 
			
		||||
				pr_err("%pOF: Can't get CPU for thread\n",
 | 
			
		||||
				       t);
 | 
			
		||||
				of_node_put(t);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			of_node_put(t);
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (t);
 | 
			
		||||
 | 
			
		||||
	cpu = get_cpu_for_node(core);
 | 
			
		||||
	if (cpu >= 0) {
 | 
			
		||||
		if (!leaf) {
 | 
			
		||||
			pr_err("%pOF: Core has both threads and CPU\n",
 | 
			
		||||
			       core);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cpu_topology[cpu].package_id = package_id;
 | 
			
		||||
		cpu_topology[cpu].core_id = core_id;
 | 
			
		||||
	} else if (leaf) {
 | 
			
		||||
		pr_err("%pOF: Can't get CPU for leaf core\n", core);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_cluster(struct device_node *cluster, int depth)
 | 
			
		||||
{
 | 
			
		||||
	char name[10];
 | 
			
		||||
	bool leaf = true;
 | 
			
		||||
	bool has_cores = false;
 | 
			
		||||
	struct device_node *c;
 | 
			
		||||
	static int package_id __initdata;
 | 
			
		||||
	int core_id = 0;
 | 
			
		||||
	int i, ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * First check for child clusters; we currently ignore any
 | 
			
		||||
	 * information about the nesting of clusters and present the
 | 
			
		||||
	 * scheduler with a flat list of them.
 | 
			
		||||
	 */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "cluster%d", i);
 | 
			
		||||
		c = of_get_child_by_name(cluster, name);
 | 
			
		||||
		if (c) {
 | 
			
		||||
			leaf = false;
 | 
			
		||||
			ret = parse_cluster(c, depth + 1);
 | 
			
		||||
			of_node_put(c);
 | 
			
		||||
			if (ret != 0)
 | 
			
		||||
				return ret;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (c);
 | 
			
		||||
 | 
			
		||||
	/* Now check for cores */
 | 
			
		||||
	i = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		snprintf(name, sizeof(name), "core%d", i);
 | 
			
		||||
		c = of_get_child_by_name(cluster, name);
 | 
			
		||||
		if (c) {
 | 
			
		||||
			has_cores = true;
 | 
			
		||||
 | 
			
		||||
			if (depth == 0) {
 | 
			
		||||
				pr_err("%pOF: cpu-map children should be clusters\n",
 | 
			
		||||
				       c);
 | 
			
		||||
				of_node_put(c);
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (leaf) {
 | 
			
		||||
				ret = parse_core(c, package_id, core_id++);
 | 
			
		||||
			} else {
 | 
			
		||||
				pr_err("%pOF: Non-leaf cluster with core %s\n",
 | 
			
		||||
				       cluster, name);
 | 
			
		||||
				ret = -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			of_node_put(c);
 | 
			
		||||
			if (ret != 0)
 | 
			
		||||
				return ret;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	} while (c);
 | 
			
		||||
 | 
			
		||||
	if (leaf && !has_cores)
 | 
			
		||||
		pr_warn("%pOF: empty cluster\n", cluster);
 | 
			
		||||
 | 
			
		||||
	if (leaf)
 | 
			
		||||
		package_id++;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init parse_dt_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cn, *map;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	cn = of_find_node_by_path("/cpus");
 | 
			
		||||
	if (!cn) {
 | 
			
		||||
		pr_err("No CPU information found in DT\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * When topology is provided cpu-map is essentially a root
 | 
			
		||||
	 * cluster with restricted subnodes.
 | 
			
		||||
	 */
 | 
			
		||||
	map = of_get_child_by_name(cn, "cpu-map");
 | 
			
		||||
	if (!map)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	ret = parse_cluster(map, 0);
 | 
			
		||||
	if (ret != 0)
 | 
			
		||||
		goto out_map;
 | 
			
		||||
 | 
			
		||||
	topology_normalize_cpu_scale();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check that all cores are in the topology; the SMP code will
 | 
			
		||||
	 * only mark cores described in the DT as possible.
 | 
			
		||||
	 */
 | 
			
		||||
	for_each_possible_cpu(cpu)
 | 
			
		||||
		if (cpu_topology[cpu].package_id == -1)
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
out_map:
 | 
			
		||||
	of_node_put(map);
 | 
			
		||||
out:
 | 
			
		||||
	of_node_put(cn);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * cpu topology table
 | 
			
		||||
 */
 | 
			
		||||
struct cpu_topology cpu_topology[NR_CPUS];
 | 
			
		||||
EXPORT_SYMBOL_GPL(cpu_topology);
 | 
			
		||||
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	const cpumask_t *core_mask = cpumask_of_node(cpu_to_node(cpu));
 | 
			
		||||
 | 
			
		||||
	/* Find the smaller of NUMA, core or LLC siblings */
 | 
			
		||||
	if (cpumask_subset(&cpu_topology[cpu].core_sibling, core_mask)) {
 | 
			
		||||
		/* not numa in package, lets use the package siblings */
 | 
			
		||||
		core_mask = &cpu_topology[cpu].core_sibling;
 | 
			
		||||
	}
 | 
			
		||||
	if (cpu_topology[cpu].llc_id != -1) {
 | 
			
		||||
		if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
 | 
			
		||||
			core_mask = &cpu_topology[cpu].llc_sibling;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return core_mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void update_siblings_masks(unsigned int cpuid)
 | 
			
		||||
{
 | 
			
		||||
	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
 | 
			
		||||
	int cpu;
 | 
			
		||||
 | 
			
		||||
	/* update core and thread sibling masks */
 | 
			
		||||
	for_each_online_cpu(cpu) {
 | 
			
		||||
		cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->llc_id == cpu_topo->llc_id) {
 | 
			
		||||
			cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
 | 
			
		||||
			cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->package_id != cpu_topo->package_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
 | 
			
		||||
		cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
 | 
			
		||||
 | 
			
		||||
		if (cpuid_topo->core_id != cpu_topo->core_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
 | 
			
		||||
		cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clear_cpu_topology(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	struct cpu_topology *cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
	cpumask_clear(&cpu_topo->llc_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->llc_sibling);
 | 
			
		||||
 | 
			
		||||
	cpumask_clear(&cpu_topo->core_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
 | 
			
		||||
	cpumask_clear(&cpu_topo->thread_sibling);
 | 
			
		||||
	cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init reset_cpu_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cpu;
 | 
			
		||||
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		struct cpu_topology *cpu_topo = &cpu_topology[cpu];
 | 
			
		||||
 | 
			
		||||
		cpu_topo->thread_id = -1;
 | 
			
		||||
		cpu_topo->core_id = -1;
 | 
			
		||||
		cpu_topo->package_id = -1;
 | 
			
		||||
		cpu_topo->llc_id = -1;
 | 
			
		||||
 | 
			
		||||
		clear_cpu_topology(cpu);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void remove_cpu_topology(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int sibling;
 | 
			
		||||
 | 
			
		||||
	for_each_cpu(sibling, topology_core_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_core_cpumask(sibling));
 | 
			
		||||
	for_each_cpu(sibling, topology_sibling_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
 | 
			
		||||
	for_each_cpu(sibling, topology_llc_cpumask(cpu))
 | 
			
		||||
		cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));
 | 
			
		||||
 | 
			
		||||
	clear_cpu_topology(cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__weak int __init parse_acpi_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
 | 
			
		||||
void __init init_cpu_topology(void)
 | 
			
		||||
{
 | 
			
		||||
	reset_cpu_topology();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Discard anything that was parsed if we hit an error so we
 | 
			
		||||
	 * don't use partial information.
 | 
			
		||||
	 */
 | 
			
		||||
	if (parse_acpi_topology())
 | 
			
		||||
		reset_cpu_topology();
 | 
			
		||||
	else if (of_have_populated_dt() && parse_dt_topology())
 | 
			
		||||
		reset_cpu_topology();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -573,3 +573,12 @@ config RANDOM_TRUST_CPU
 | 
			
		||||
	has not installed a hidden back door to compromise the CPU's
 | 
			
		||||
	random number generation facilities. This can also be configured
 | 
			
		||||
	at boot with "random.trust_cpu=on/off".
 | 
			
		||||
 | 
			
		||||
config RANDOM_TRUST_BOOTLOADER
 | 
			
		||||
	bool "Trust the bootloader to initialize Linux's CRNG"
 | 
			
		||||
	help
 | 
			
		||||
	Some bootloaders can provide entropy to increase the kernel's initial
 | 
			
		||||
	device randomness. Say Y here to assume the entropy provided by the
 | 
			
		||||
	booloader is trustworthy so it will be added to the kernel's entropy
 | 
			
		||||
	pool. Otherwise, say N here so it will be regarded as device input that
 | 
			
		||||
	only mixes the entropy pool.
 | 
			
		||||
@ -2445,3 +2445,17 @@ void add_hwgenerator_randomness(const char *buffer, size_t count,
 | 
			
		||||
	credit_entropy_bits(poolp, entropy);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(add_hwgenerator_randomness);
 | 
			
		||||
 | 
			
		||||
/* Handle random seed passed by bootloader.
 | 
			
		||||
 * If the seed is trustworthy, it would be regarded as hardware RNGs. Otherwise
 | 
			
		||||
 * it would be regarded as device data.
 | 
			
		||||
 * The decision is controlled by CONFIG_RANDOM_TRUST_BOOTLOADER.
 | 
			
		||||
 */
 | 
			
		||||
void add_bootloader_randomness(const void *buf, unsigned int size)
 | 
			
		||||
{
 | 
			
		||||
	if (IS_ENABLED(CONFIG_RANDOM_TRUST_BOOTLOADER))
 | 
			
		||||
		add_hwgenerator_randomness(buf, size, size * 8);
 | 
			
		||||
	else
 | 
			
		||||
		add_device_randomness(buf, size);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(add_bootloader_randomness);
 | 
			
		||||
@ -13,6 +13,16 @@ config ARM_CPUIDLE
 | 
			
		||||
          initialized by calling the CPU operations init idle hook
 | 
			
		||||
          provided by architecture code.
 | 
			
		||||
 | 
			
		||||
config ARM_PSCI_CPUIDLE
 | 
			
		||||
	bool "PSCI CPU idle Driver"
 | 
			
		||||
	depends on ARM_PSCI_FW
 | 
			
		||||
	select DT_IDLE_STATES
 | 
			
		||||
	select CPU_IDLE_MULTIPLE_DRIVERS
 | 
			
		||||
	help
 | 
			
		||||
	  Select this to enable PSCI firmware based CPUidle driver for ARM.
 | 
			
		||||
	  It provides an idle driver that is capable of detecting and
 | 
			
		||||
	  managing idle states through the PSCI firmware interface.
 | 
			
		||||
 | 
			
		||||
config ARM_BIG_LITTLE_CPUIDLE
 | 
			
		||||
	bool "Support for ARM big.LITTLE processors"
 | 
			
		||||
	depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
 | 
			
		||||
obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
 | 
			
		||||
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
 | 
			
		||||
obj-$(CONFIG_ARM_CPUIDLE)		+= cpuidle-arm.o
 | 
			
		||||
obj-$(CONFIG_ARM_PSCI_CPUIDLE)		+= cpuidle-psci.o
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# MIPS drivers
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,6 @@
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/topology.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/cpuidle.h>
 | 
			
		||||
 | 
			
		||||
@ -106,11 +105,17 @@ static int __init arm_idle_init_cpu(int cpu)
 | 
			
		||||
	ret = arm_cpuidle_init(cpu);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Allow the initialization to continue for other CPUs, if the reported
 | 
			
		||||
	 * failure is a HW misconfiguration/breakage (-ENXIO).
 | 
			
		||||
	 * Allow the initialization to continue for other CPUs, if the
 | 
			
		||||
	 * reported failure is a HW misconfiguration/breakage (-ENXIO).
 | 
			
		||||
	 *
 | 
			
		||||
	 * Some platforms do not support idle operations
 | 
			
		||||
	 * (arm_cpuidle_init() returning -EOPNOTSUPP), we should
 | 
			
		||||
	 * not flag this case as an error, it is a valid
 | 
			
		||||
	 * configuration.
 | 
			
		||||
	 */
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("CPU %d failed to init idle CPU ops\n", cpu);
 | 
			
		||||
		if (ret != -EOPNOTSUPP)
 | 
			
		||||
			pr_err("CPU %d failed to init idle CPU ops\n", cpu);
 | 
			
		||||
		ret = ret == -ENXIO ? 0 : ret;
 | 
			
		||||
		goto out_kfree_drv;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										236
									
								
								drivers/cpuidle/cpuidle-psci.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								drivers/cpuidle/cpuidle-psci.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,236 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * PSCI CPU idle driver.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019 ARM Ltd.
 | 
			
		||||
 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt) "CPUidle PSCI: " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/cpuidle.h>
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
#include <linux/cpu_pm.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/of_device.h>
 | 
			
		||||
#include <linux/psci.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/cpuidle.h>
 | 
			
		||||
 | 
			
		||||
#include "dt_idle_states.h"
 | 
			
		||||
 | 
			
		||||
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
 | 
			
		||||
 | 
			
		||||
static int psci_enter_idle_state(struct cpuidle_device *dev,
 | 
			
		||||
				struct cpuidle_driver *drv, int idx)
 | 
			
		||||
{
 | 
			
		||||
	u32 *state = __this_cpu_read(psci_power_state);
 | 
			
		||||
 | 
			
		||||
	return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter,
 | 
			
		||||
					   idx, state[idx - 1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cpuidle_driver psci_idle_driver __initdata = {
 | 
			
		||||
	.name = "psci_idle",
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	/*
 | 
			
		||||
	 * PSCI idle states relies on architectural WFI to
 | 
			
		||||
	 * be represented as state index 0.
 | 
			
		||||
	 */
 | 
			
		||||
	.states[0] = {
 | 
			
		||||
		.enter                  = psci_enter_idle_state,
 | 
			
		||||
		.exit_latency           = 1,
 | 
			
		||||
		.target_residency       = 1,
 | 
			
		||||
		.power_usage		= UINT_MAX,
 | 
			
		||||
		.name                   = "WFI",
 | 
			
		||||
		.desc                   = "ARM WFI",
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id psci_idle_state_match[] __initconst = {
 | 
			
		||||
	{ .compatible = "arm,idle-state",
 | 
			
		||||
	  .data = psci_enter_idle_state },
 | 
			
		||||
	{ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
 | 
			
		||||
{
 | 
			
		||||
	int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
 | 
			
		||||
 | 
			
		||||
	if (err) {
 | 
			
		||||
		pr_warn("%pOF missing arm,psci-suspend-param property\n", np);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!psci_power_state_is_valid(*state)) {
 | 
			
		||||
		pr_warn("Invalid PSCI power state %#x\n", *state);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int i, ret = 0, count = 0;
 | 
			
		||||
	u32 *psci_states;
 | 
			
		||||
	struct device_node *state_node;
 | 
			
		||||
 | 
			
		||||
	/* Count idle states */
 | 
			
		||||
	while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
 | 
			
		||||
					      count))) {
 | 
			
		||||
		count++;
 | 
			
		||||
		of_node_put(state_node);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!count)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
 | 
			
		||||
	if (!psci_states)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; i++) {
 | 
			
		||||
		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
 | 
			
		||||
		ret = psci_dt_parse_state_node(state_node, &psci_states[i]);
 | 
			
		||||
		of_node_put(state_node);
 | 
			
		||||
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto free_mem;
 | 
			
		||||
 | 
			
		||||
		pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Idle states parsed correctly, initialize per-cpu pointer */
 | 
			
		||||
	per_cpu(psci_power_state, cpu) = psci_states;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
free_mem:
 | 
			
		||||
	kfree(psci_states);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static __init int psci_cpu_init_idle(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu_node;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the PSCI cpu_suspend function hook has not been initialized
 | 
			
		||||
	 * idle states must not be enabled, so bail out
 | 
			
		||||
	 */
 | 
			
		||||
	if (!psci_ops.cpu_suspend)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	cpu_node = of_cpu_device_node_get(cpu);
 | 
			
		||||
	if (!cpu_node)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	ret = psci_dt_cpu_init_idle(cpu_node, cpu);
 | 
			
		||||
 | 
			
		||||
	of_node_put(cpu_node);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init psci_idle_init_cpu(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	struct cpuidle_driver *drv;
 | 
			
		||||
	struct device_node *cpu_node;
 | 
			
		||||
	const char *enable_method;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	cpu_node = of_cpu_device_node_get(cpu);
 | 
			
		||||
	if (!cpu_node)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check whether the enable-method for the cpu is PSCI, fail
 | 
			
		||||
	 * if it is not.
 | 
			
		||||
	 */
 | 
			
		||||
	enable_method = of_get_property(cpu_node, "enable-method", NULL);
 | 
			
		||||
	if (!enable_method || (strcmp(enable_method, "psci")))
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
 | 
			
		||||
	of_node_put(cpu_node);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	drv = kmemdup(&psci_idle_driver, sizeof(*drv), GFP_KERNEL);
 | 
			
		||||
	if (!drv)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	drv->cpumask = (struct cpumask *)cpumask_of(cpu);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Initialize idle states data, starting at index 1, since
 | 
			
		||||
	 * by default idle state 0 is the quiescent state reached
 | 
			
		||||
	 * by the cpu by executing the wfi instruction.
 | 
			
		||||
	 *
 | 
			
		||||
	 * If no DT idle states are detected (ret == 0) let the driver
 | 
			
		||||
	 * initialization fail accordingly since there is no reason to
 | 
			
		||||
	 * initialize the idle driver if only wfi is supported, the
 | 
			
		||||
	 * default archictectural back-end already executes wfi
 | 
			
		||||
	 * on idle entry.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = dt_init_idle_driver(drv, psci_idle_state_match, 1);
 | 
			
		||||
	if (ret <= 0) {
 | 
			
		||||
		ret = ret ? : -ENODEV;
 | 
			
		||||
		goto out_kfree_drv;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Initialize PSCI idle states.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = psci_cpu_init_idle(cpu);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("CPU %d failed to PSCI idle\n", cpu);
 | 
			
		||||
		goto out_kfree_drv;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = cpuidle_register(drv, NULL);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out_kfree_drv;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_kfree_drv:
 | 
			
		||||
	kfree(drv);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * psci_idle_init - Initializes PSCI cpuidle driver
 | 
			
		||||
 *
 | 
			
		||||
 * Initializes PSCI cpuidle driver for all CPUs, if any CPU fails
 | 
			
		||||
 * to register cpuidle driver then rollback to cancel all CPUs
 | 
			
		||||
 * registration.
 | 
			
		||||
 */
 | 
			
		||||
static int __init psci_idle_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int cpu, ret;
 | 
			
		||||
	struct cpuidle_driver *drv;
 | 
			
		||||
	struct cpuidle_device *dev;
 | 
			
		||||
 | 
			
		||||
	for_each_possible_cpu(cpu) {
 | 
			
		||||
		ret = psci_idle_init_cpu(cpu);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto out_fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_fail:
 | 
			
		||||
	while (--cpu >= 0) {
 | 
			
		||||
		dev = per_cpu(cpuidle_devices, cpu);
 | 
			
		||||
		drv = cpuidle_get_cpu_driver(dev);
 | 
			
		||||
		cpuidle_unregister(drv);
 | 
			
		||||
		kfree(drv);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
device_initcall(psci_idle_init);
 | 
			
		||||
@ -103,7 +103,7 @@ static inline bool psci_power_state_loses_context(u32 state)
 | 
			
		||||
	return state & mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool psci_power_state_is_valid(u32 state)
 | 
			
		||||
bool psci_power_state_is_valid(u32 state)
 | 
			
		||||
{
 | 
			
		||||
	const u32 valid_mask = psci_has_ext_power_state() ?
 | 
			
		||||
			       PSCI_1_0_EXT_POWER_STATE_MASK :
 | 
			
		||||
@ -277,175 +277,24 @@ static int __init psci_features(u32 psci_func_id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_CPU_IDLE
 | 
			
		||||
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
 | 
			
		||||
 | 
			
		||||
static int psci_dt_parse_state_node(struct device_node *np, u32 *state)
 | 
			
		||||
static int psci_suspend_finisher(unsigned long state)
 | 
			
		||||
{
 | 
			
		||||
	int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
 | 
			
		||||
	u32 power_state = state;
 | 
			
		||||
 | 
			
		||||
	if (err) {
 | 
			
		||||
		pr_warn("%pOF missing arm,psci-suspend-param property\n", np);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!psci_power_state_is_valid(*state)) {
 | 
			
		||||
		pr_warn("Invalid PSCI power state %#x\n", *state);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return psci_ops.cpu_suspend(power_state, __pa_symbol(cpu_resume));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
 | 
			
		||||
int psci_cpu_suspend_enter(u32 state)
 | 
			
		||||
{
 | 
			
		||||
	int i, ret = 0, count = 0;
 | 
			
		||||
	u32 *psci_states;
 | 
			
		||||
	struct device_node *state_node;
 | 
			
		||||
 | 
			
		||||
	/* Count idle states */
 | 
			
		||||
	while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
 | 
			
		||||
					      count))) {
 | 
			
		||||
		count++;
 | 
			
		||||
		of_node_put(state_node);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!count)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
 | 
			
		||||
	if (!psci_states)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; i++) {
 | 
			
		||||
		state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
 | 
			
		||||
		ret = psci_dt_parse_state_node(state_node, &psci_states[i]);
 | 
			
		||||
		of_node_put(state_node);
 | 
			
		||||
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto free_mem;
 | 
			
		||||
 | 
			
		||||
		pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Idle states parsed correctly, initialize per-cpu pointer */
 | 
			
		||||
	per_cpu(psci_power_state, cpu) = psci_states;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
free_mem:
 | 
			
		||||
	kfree(psci_states);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ACPI
 | 
			
		||||
#include <acpi/processor.h>
 | 
			
		||||
 | 
			
		||||
static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int i, count;
 | 
			
		||||
	u32 *psci_states;
 | 
			
		||||
	struct acpi_lpi_state *lpi;
 | 
			
		||||
	struct acpi_processor *pr = per_cpu(processors, cpu);
 | 
			
		||||
 | 
			
		||||
	if (unlikely(!pr || !pr->flags.has_lpi))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	count = pr->power.count - 1;
 | 
			
		||||
	if (count <= 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
 | 
			
		||||
	if (!psci_states)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < count; i++) {
 | 
			
		||||
		u32 state;
 | 
			
		||||
 | 
			
		||||
		lpi = &pr->power.lpi_states[i + 1];
 | 
			
		||||
		/*
 | 
			
		||||
		 * Only bits[31:0] represent a PSCI power_state while
 | 
			
		||||
		 * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
 | 
			
		||||
		 */
 | 
			
		||||
		state = lpi->address;
 | 
			
		||||
		if (!psci_power_state_is_valid(state)) {
 | 
			
		||||
			pr_warn("Invalid PSCI power state %#x\n", state);
 | 
			
		||||
			kfree(psci_states);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		psci_states[i] = state;
 | 
			
		||||
	}
 | 
			
		||||
	/* Idle states parsed correctly, initialize per-cpu pointer */
 | 
			
		||||
	per_cpu(psci_power_state, cpu) = psci_states;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int psci_cpu_init_idle(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu_node;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the PSCI cpu_suspend function hook has not been initialized
 | 
			
		||||
	 * idle states must not be enabled, so bail out
 | 
			
		||||
	 */
 | 
			
		||||
	if (!psci_ops.cpu_suspend)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (!acpi_disabled)
 | 
			
		||||
		return psci_acpi_cpu_init_idle(cpu);
 | 
			
		||||
 | 
			
		||||
	cpu_node = of_get_cpu_node(cpu, NULL);
 | 
			
		||||
	if (!cpu_node)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	ret = psci_dt_cpu_init_idle(cpu_node, cpu);
 | 
			
		||||
 | 
			
		||||
	of_node_put(cpu_node);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psci_suspend_finisher(unsigned long index)
 | 
			
		||||
{
 | 
			
		||||
	u32 *state = __this_cpu_read(psci_power_state);
 | 
			
		||||
 | 
			
		||||
	return psci_ops.cpu_suspend(state[index - 1],
 | 
			
		||||
				    __pa_symbol(cpu_resume));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int psci_cpu_suspend_enter(unsigned long index)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	u32 *state = __this_cpu_read(psci_power_state);
 | 
			
		||||
	/*
 | 
			
		||||
	 * idle state index 0 corresponds to wfi, should never be called
 | 
			
		||||
	 * from the cpu_suspend operations
 | 
			
		||||
	 */
 | 
			
		||||
	if (WARN_ON_ONCE(!index))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!psci_power_state_loses_context(state[index - 1]))
 | 
			
		||||
		ret = psci_ops.cpu_suspend(state[index - 1], 0);
 | 
			
		||||
	if (!psci_power_state_loses_context(state))
 | 
			
		||||
		ret = psci_ops.cpu_suspend(state, 0);
 | 
			
		||||
	else
 | 
			
		||||
		ret = cpu_suspend(index, psci_suspend_finisher);
 | 
			
		||||
		ret = cpu_suspend(state, psci_suspend_finisher);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ARM specific CPU idle operations */
 | 
			
		||||
#ifdef CONFIG_ARM
 | 
			
		||||
static const struct cpuidle_ops psci_cpuidle_ops __initconst = {
 | 
			
		||||
	.suspend = psci_cpu_suspend_enter,
 | 
			
		||||
	.init = psci_dt_cpu_init_idle,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CPUIDLE_METHOD_OF_DECLARE(psci, "psci", &psci_cpuidle_ops);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int psci_system_suspend(unsigned long unused)
 | 
			
		||||
 | 
			
		||||
@ -228,8 +228,11 @@ out_free_cpus:
 | 
			
		||||
 | 
			
		||||
static void dummy_callback(struct timer_list *unused) {}
 | 
			
		||||
 | 
			
		||||
static int suspend_cpu(int index, bool broadcast)
 | 
			
		||||
static int suspend_cpu(struct cpuidle_device *dev,
 | 
			
		||||
		       struct cpuidle_driver *drv, int index)
 | 
			
		||||
{
 | 
			
		||||
	struct cpuidle_state *state = &drv->states[index];
 | 
			
		||||
	bool broadcast = state->flags & CPUIDLE_FLAG_TIMER_STOP;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	arch_cpu_idle_enter();
 | 
			
		||||
@ -254,11 +257,7 @@ static int suspend_cpu(int index, bool broadcast)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Replicate the common ARM cpuidle enter function
 | 
			
		||||
	 * (arm_enter_idle_state).
 | 
			
		||||
	 */
 | 
			
		||||
	ret = CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, index);
 | 
			
		||||
	ret = state->enter(dev, drv, index);
 | 
			
		||||
 | 
			
		||||
	if (broadcast)
 | 
			
		||||
		tick_broadcast_exit();
 | 
			
		||||
@ -301,9 +300,8 @@ static int suspend_test_thread(void *arg)
 | 
			
		||||
		 * doesn't use PSCI).
 | 
			
		||||
		 */
 | 
			
		||||
		for (index = 1; index < drv->state_count; ++index) {
 | 
			
		||||
			struct cpuidle_state *state = &drv->states[index];
 | 
			
		||||
			bool broadcast = state->flags & CPUIDLE_FLAG_TIMER_STOP;
 | 
			
		||||
			int ret;
 | 
			
		||||
			struct cpuidle_state *state = &drv->states[index];
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Set the timer to wake this CPU up in some time (which
 | 
			
		||||
@ -318,7 +316,7 @@ static int suspend_test_thread(void *arg)
 | 
			
		||||
			/* IRQs must be disabled during suspend operations. */
 | 
			
		||||
			local_irq_disable();
 | 
			
		||||
 | 
			
		||||
			ret = suspend_cpu(index, broadcast);
 | 
			
		||||
			ret = suspend_cpu(dev, drv, index);
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * We have woken up. Re-enable IRQs to handle any
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@
 | 
			
		||||
#include <linux/debugfs.h>
 | 
			
		||||
#include <linux/serial_core.h>
 | 
			
		||||
#include <linux/sysfs.h>
 | 
			
		||||
#include <linux/random.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
@ -1044,6 +1045,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
 | 
			
		||||
{
 | 
			
		||||
	int l;
 | 
			
		||||
	const char *p;
 | 
			
		||||
	const void *rng_seed;
 | 
			
		||||
 | 
			
		||||
	pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
 | 
			
		||||
 | 
			
		||||
@ -1078,6 +1080,18 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
 | 
			
		||||
 | 
			
		||||
	pr_debug("Command line is: %s\n", (char*)data);
 | 
			
		||||
 | 
			
		||||
	rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);
 | 
			
		||||
	if (rng_seed && l > 0) {
 | 
			
		||||
		add_bootloader_randomness(rng_seed, l);
 | 
			
		||||
 | 
			
		||||
		/* try to clear seed so it won't be found. */
 | 
			
		||||
		fdt_nop_property(initial_boot_params, node, "rng-seed");
 | 
			
		||||
 | 
			
		||||
		/* update CRC check value */
 | 
			
		||||
		of_fdt_crc32 = crc32_be(~0, initial_boot_params,
 | 
			
		||||
				fdt_totalsize(initial_boot_params));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* break now */
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -113,8 +113,6 @@ struct smmu_pmu {
 | 
			
		||||
	u64 counter_mask;
 | 
			
		||||
	u32 options;
 | 
			
		||||
	bool global_filter;
 | 
			
		||||
	u32 global_filter_span;
 | 
			
		||||
	u32 global_filter_sid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define to_smmu_pmu(p) (container_of(p, struct smmu_pmu, pmu))
 | 
			
		||||
@ -260,6 +258,19 @@ static void smmu_pmu_set_event_filter(struct perf_event *event,
 | 
			
		||||
	smmu_pmu_set_smr(smmu_pmu, idx, sid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool smmu_pmu_check_global_filter(struct perf_event *curr,
 | 
			
		||||
					 struct perf_event *new)
 | 
			
		||||
{
 | 
			
		||||
	if (get_filter_enable(new) != get_filter_enable(curr))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!get_filter_enable(new))
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	return get_filter_span(new) == get_filter_span(curr) &&
 | 
			
		||||
	       get_filter_stream_id(new) == get_filter_stream_id(curr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int smmu_pmu_apply_event_filter(struct smmu_pmu *smmu_pmu,
 | 
			
		||||
				       struct perf_event *event, int idx)
 | 
			
		||||
{
 | 
			
		||||
@ -279,17 +290,14 @@ static int smmu_pmu_apply_event_filter(struct smmu_pmu *smmu_pmu,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Requested settings same as current global settings*/
 | 
			
		||||
	if (span == smmu_pmu->global_filter_span &&
 | 
			
		||||
	    sid == smmu_pmu->global_filter_sid)
 | 
			
		||||
	idx = find_first_bit(smmu_pmu->used_counters, num_ctrs);
 | 
			
		||||
	if (idx == num_ctrs ||
 | 
			
		||||
	    smmu_pmu_check_global_filter(smmu_pmu->events[idx], event)) {
 | 
			
		||||
		smmu_pmu_set_event_filter(event, 0, span, sid);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!bitmap_empty(smmu_pmu->used_counters, num_ctrs))
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
 | 
			
		||||
	smmu_pmu_set_event_filter(event, 0, span, sid);
 | 
			
		||||
	smmu_pmu->global_filter_span = span;
 | 
			
		||||
	smmu_pmu->global_filter_sid = sid;
 | 
			
		||||
	return 0;
 | 
			
		||||
	return -EAGAIN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int smmu_pmu_get_event_idx(struct smmu_pmu *smmu_pmu,
 | 
			
		||||
@ -312,6 +320,19 @@ static int smmu_pmu_get_event_idx(struct smmu_pmu *smmu_pmu,
 | 
			
		||||
	return idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool smmu_pmu_events_compatible(struct perf_event *curr,
 | 
			
		||||
				       struct perf_event *new)
 | 
			
		||||
{
 | 
			
		||||
	if (new->pmu != curr->pmu)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (to_smmu_pmu(new->pmu)->global_filter &&
 | 
			
		||||
	    !smmu_pmu_check_global_filter(curr, new))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Implementation of abstract pmu functionality required by
 | 
			
		||||
 * the core perf events code.
 | 
			
		||||
@ -323,6 +344,7 @@ static int smmu_pmu_event_init(struct perf_event *event)
 | 
			
		||||
	struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
 | 
			
		||||
	struct device *dev = smmu_pmu->dev;
 | 
			
		||||
	struct perf_event *sibling;
 | 
			
		||||
	int group_num_events = 1;
 | 
			
		||||
	u16 event_id;
 | 
			
		||||
 | 
			
		||||
	if (event->attr.type != event->pmu->type)
 | 
			
		||||
@ -347,18 +369,23 @@ static int smmu_pmu_event_init(struct perf_event *event)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Don't allow groups with mixed PMUs, except for s/w events */
 | 
			
		||||
	if (event->group_leader->pmu != event->pmu &&
 | 
			
		||||
	    !is_software_event(event->group_leader)) {
 | 
			
		||||
		dev_dbg(dev, "Can't create mixed PMU group\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (!is_software_event(event->group_leader)) {
 | 
			
		||||
		if (!smmu_pmu_events_compatible(event->group_leader, event))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		if (++group_num_events > smmu_pmu->num_counters)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for_each_sibling_event(sibling, event->group_leader) {
 | 
			
		||||
		if (sibling->pmu != event->pmu &&
 | 
			
		||||
		    !is_software_event(sibling)) {
 | 
			
		||||
			dev_dbg(dev, "Can't create mixed PMU group\n");
 | 
			
		||||
		if (is_software_event(sibling))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!smmu_pmu_events_compatible(sibling, event))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		if (++group_num_events > smmu_pmu->num_counters)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hwc->idx = -1;
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@
 | 
			
		||||
#define EVENT_CYCLES_COUNTER	0
 | 
			
		||||
#define NUM_COUNTERS		4
 | 
			
		||||
 | 
			
		||||
#define AXI_MASKING_REVERT	0xffff0000	/* AXI_MASKING(MSB 16bits) + AXI_ID(LSB 16bits) */
 | 
			
		||||
 | 
			
		||||
#define to_ddr_pmu(p)		container_of(p, struct ddr_pmu, pmu)
 | 
			
		||||
 | 
			
		||||
#define DDR_PERF_DEV_NAME	"imx8_ddr"
 | 
			
		||||
@ -42,11 +44,25 @@
 | 
			
		||||
 | 
			
		||||
static DEFINE_IDA(ddr_ida);
 | 
			
		||||
 | 
			
		||||
/* DDR Perf hardware feature */
 | 
			
		||||
#define DDR_CAP_AXI_ID_FILTER          0x1     /* support AXI ID filter */
 | 
			
		||||
 | 
			
		||||
struct fsl_ddr_devtype_data {
 | 
			
		||||
	unsigned int quirks;    /* quirks needed for different DDR Perf core */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct fsl_ddr_devtype_data imx8_devtype_data;
 | 
			
		||||
 | 
			
		||||
static const struct fsl_ddr_devtype_data imx8m_devtype_data = {
 | 
			
		||||
	.quirks = DDR_CAP_AXI_ID_FILTER,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
 | 
			
		||||
	{ .compatible = "fsl,imx8-ddr-pmu",},
 | 
			
		||||
	{ .compatible = "fsl,imx8m-ddr-pmu",},
 | 
			
		||||
	{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
 | 
			
		||||
	{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
 | 
			
		||||
	{ /* sentinel */ }
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, imx_ddr_pmu_dt_ids);
 | 
			
		||||
 | 
			
		||||
struct ddr_pmu {
 | 
			
		||||
	struct pmu pmu;
 | 
			
		||||
@ -57,6 +73,7 @@ struct ddr_pmu {
 | 
			
		||||
	struct perf_event *events[NUM_COUNTERS];
 | 
			
		||||
	int active_events;
 | 
			
		||||
	enum cpuhp_state cpuhp_state;
 | 
			
		||||
	const struct fsl_ddr_devtype_data *devtype_data;
 | 
			
		||||
	int irq;
 | 
			
		||||
	int id;
 | 
			
		||||
};
 | 
			
		||||
@ -128,6 +145,8 @@ static struct attribute *ddr_perf_events_attrs[] = {
 | 
			
		||||
	IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37),
 | 
			
		||||
	IMX8_DDR_PMU_EVENT_ATTR(write, 0x38),
 | 
			
		||||
	IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39),
 | 
			
		||||
	IMX8_DDR_PMU_EVENT_ATTR(axid-read, 0x41),
 | 
			
		||||
	IMX8_DDR_PMU_EVENT_ATTR(axid-write, 0x42),
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -137,9 +156,13 @@ static struct attribute_group ddr_perf_events_attr_group = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PMU_FORMAT_ATTR(event, "config:0-7");
 | 
			
		||||
PMU_FORMAT_ATTR(axi_id, "config1:0-15");
 | 
			
		||||
PMU_FORMAT_ATTR(axi_mask, "config1:16-31");
 | 
			
		||||
 | 
			
		||||
static struct attribute *ddr_perf_format_attrs[] = {
 | 
			
		||||
	&format_attr_event.attr,
 | 
			
		||||
	&format_attr_axi_id.attr,
 | 
			
		||||
	&format_attr_axi_mask.attr,
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -189,6 +212,26 @@ static u32 ddr_perf_read_counter(struct ddr_pmu *pmu, int counter)
 | 
			
		||||
	return readl_relaxed(pmu->base + COUNTER_READ + counter * 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ddr_perf_is_filtered(struct perf_event *event)
 | 
			
		||||
{
 | 
			
		||||
	return event->attr.config == 0x41 || event->attr.config == 0x42;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 ddr_perf_filter_val(struct perf_event *event)
 | 
			
		||||
{
 | 
			
		||||
	return event->attr.config1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ddr_perf_filters_compatible(struct perf_event *a,
 | 
			
		||||
					struct perf_event *b)
 | 
			
		||||
{
 | 
			
		||||
	if (!ddr_perf_is_filtered(a))
 | 
			
		||||
		return true;
 | 
			
		||||
	if (!ddr_perf_is_filtered(b))
 | 
			
		||||
		return true;
 | 
			
		||||
	return ddr_perf_filter_val(a) == ddr_perf_filter_val(b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ddr_perf_event_init(struct perf_event *event)
 | 
			
		||||
{
 | 
			
		||||
	struct ddr_pmu *pmu = to_ddr_pmu(event->pmu);
 | 
			
		||||
@ -215,6 +258,15 @@ static int ddr_perf_event_init(struct perf_event *event)
 | 
			
		||||
			!is_software_event(event->group_leader))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
 | 
			
		||||
		if (!ddr_perf_filters_compatible(event, event->group_leader))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		for_each_sibling_event(sibling, event->group_leader) {
 | 
			
		||||
			if (!ddr_perf_filters_compatible(event, sibling))
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for_each_sibling_event(sibling, event->group_leader) {
 | 
			
		||||
		if (sibling->pmu != event->pmu &&
 | 
			
		||||
				!is_software_event(sibling))
 | 
			
		||||
@ -287,6 +339,23 @@ static int ddr_perf_event_add(struct perf_event *event, int flags)
 | 
			
		||||
	struct hw_perf_event *hwc = &event->hw;
 | 
			
		||||
	int counter;
 | 
			
		||||
	int cfg = event->attr.config;
 | 
			
		||||
	int cfg1 = event->attr.config1;
 | 
			
		||||
 | 
			
		||||
	if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) {
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		for (i = 1; i < NUM_COUNTERS; i++) {
 | 
			
		||||
			if (pmu->events[i] &&
 | 
			
		||||
			    !ddr_perf_filters_compatible(event, pmu->events[i]))
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (ddr_perf_is_filtered(event)) {
 | 
			
		||||
			/* revert axi id masking(axi_mask) value */
 | 
			
		||||
			cfg1 ^= AXI_MASKING_REVERT;
 | 
			
		||||
			writel(cfg1, pmu->base + COUNTER_DPCR1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	counter = ddr_perf_alloc_counter(pmu, cfg);
 | 
			
		||||
	if (counter < 0) {
 | 
			
		||||
@ -472,6 +541,8 @@ static int ddr_perf_probe(struct platform_device *pdev)
 | 
			
		||||
	if (!name)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	pmu->devtype_data = of_device_get_match_data(&pdev->dev);
 | 
			
		||||
 | 
			
		||||
	pmu->cpu = raw_smp_processor_id();
 | 
			
		||||
	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
 | 
			
		||||
				      DDR_CPUHP_CB_NAME,
 | 
			
		||||
 | 
			
		||||
@ -217,10 +217,8 @@ static int hisi_ddrc_pmu_init_irq(struct hisi_pmu *ddrc_pmu,
 | 
			
		||||
 | 
			
		||||
	/* Read and init IRQ */
 | 
			
		||||
	irq = platform_get_irq(pdev, 0);
 | 
			
		||||
	if (irq < 0) {
 | 
			
		||||
		dev_err(&pdev->dev, "DDRC PMU get irq fail; irq:%d\n", irq);
 | 
			
		||||
	if (irq < 0)
 | 
			
		||||
		return irq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = devm_request_irq(&pdev->dev, irq, hisi_ddrc_pmu_isr,
 | 
			
		||||
			       IRQF_NOBALANCING | IRQF_NO_THREAD,
 | 
			
		||||
 | 
			
		||||
@ -207,10 +207,8 @@ static int hisi_hha_pmu_init_irq(struct hisi_pmu *hha_pmu,
 | 
			
		||||
 | 
			
		||||
	/* Read and init IRQ */
 | 
			
		||||
	irq = platform_get_irq(pdev, 0);
 | 
			
		||||
	if (irq < 0) {
 | 
			
		||||
		dev_err(&pdev->dev, "HHA PMU get irq fail; irq:%d\n", irq);
 | 
			
		||||
	if (irq < 0)
 | 
			
		||||
		return irq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = devm_request_irq(&pdev->dev, irq, hisi_hha_pmu_isr,
 | 
			
		||||
			      IRQF_NOBALANCING | IRQF_NO_THREAD,
 | 
			
		||||
 | 
			
		||||
@ -206,10 +206,8 @@ static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu,
 | 
			
		||||
 | 
			
		||||
	/* Read and init IRQ */
 | 
			
		||||
	irq = platform_get_irq(pdev, 0);
 | 
			
		||||
	if (irq < 0) {
 | 
			
		||||
		dev_err(&pdev->dev, "L3C PMU get irq fail; irq:%d\n", irq);
 | 
			
		||||
	if (irq < 0)
 | 
			
		||||
		return irq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr,
 | 
			
		||||
			       IRQF_NOBALANCING | IRQF_NO_THREAD,
 | 
			
		||||
 | 
			
		||||
@ -909,12 +909,8 @@ static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
 | 
			
		||||
	cluster->cluster_id = fw_cluster_id;
 | 
			
		||||
 | 
			
		||||
	irq = platform_get_irq(sdev, 0);
 | 
			
		||||
	if (irq < 0) {
 | 
			
		||||
		dev_err(&pdev->dev,
 | 
			
		||||
			"Failed to get valid irq for cluster %ld\n",
 | 
			
		||||
			fw_cluster_id);
 | 
			
		||||
	if (irq < 0)
 | 
			
		||||
		return irq;
 | 
			
		||||
	}
 | 
			
		||||
	irq_set_status_flags(irq, IRQ_NOAUTOEN);
 | 
			
		||||
	cluster->irq = irq;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1901,10 +1901,8 @@ static int xgene_pmu_probe(struct platform_device *pdev)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	irq = platform_get_irq(pdev, 0);
 | 
			
		||||
	if (irq < 0) {
 | 
			
		||||
		dev_err(&pdev->dev, "No IRQ resource\n");
 | 
			
		||||
	if (irq < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr,
 | 
			
		||||
				IRQF_NOBALANCING | IRQF_NO_THREAD,
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@ struct error_injection_entry {
 | 
			
		||||
	int		etype;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pt_regs;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
			
		||||
/*
 | 
			
		||||
 * Whitelist ganerating macro. Specify functions which can be
 | 
			
		||||
@ -28,8 +30,12 @@ static struct error_injection_entry __used				\
 | 
			
		||||
		.addr = (unsigned long)fname,				\
 | 
			
		||||
		.etype = EI_ETYPE_##_etype,				\
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
void override_function_with_return(struct pt_regs *regs);
 | 
			
		||||
#else
 | 
			
		||||
#define ALLOW_ERROR_INJECTION(fname, _etype)
 | 
			
		||||
 | 
			
		||||
static inline void override_function_with_return(struct pt_regs *regs) { }
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1302,11 +1302,16 @@ static inline int lpit_read_residency_count_address(u64 *address)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ACPI_PPTT
 | 
			
		||||
int acpi_pptt_cpu_is_thread(unsigned int cpu);
 | 
			
		||||
int find_acpi_cpu_topology(unsigned int cpu, int level);
 | 
			
		||||
int find_acpi_cpu_topology_package(unsigned int cpu);
 | 
			
		||||
int find_acpi_cpu_topology_hetero_id(unsigned int cpu);
 | 
			
		||||
int find_acpi_cpu_cache_topology(unsigned int cpu, int level);
 | 
			
		||||
#else
 | 
			
		||||
static inline int acpi_pptt_cpu_is_thread(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
static inline int find_acpi_cpu_topology(unsigned int cpu, int level)
 | 
			
		||||
{
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
 | 
			
		||||
@ -33,4 +33,30 @@ unsigned long topology_get_freq_scale(int cpu)
 | 
			
		||||
	return per_cpu(freq_scale, cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cpu_topology {
 | 
			
		||||
	int thread_id;
 | 
			
		||||
	int core_id;
 | 
			
		||||
	int package_id;
 | 
			
		||||
	int llc_id;
 | 
			
		||||
	cpumask_t thread_sibling;
 | 
			
		||||
	cpumask_t core_sibling;
 | 
			
		||||
	cpumask_t llc_sibling;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY
 | 
			
		||||
extern struct cpu_topology cpu_topology[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
#define topology_physical_package_id(cpu)	(cpu_topology[cpu].package_id)
 | 
			
		||||
#define topology_core_id(cpu)		(cpu_topology[cpu].core_id)
 | 
			
		||||
#define topology_core_cpumask(cpu)	(&cpu_topology[cpu].core_sibling)
 | 
			
		||||
#define topology_sibling_cpumask(cpu)	(&cpu_topology[cpu].thread_sibling)
 | 
			
		||||
#define topology_llc_cpumask(cpu)	(&cpu_topology[cpu].llc_sibling)
 | 
			
		||||
void init_cpu_topology(void);
 | 
			
		||||
void store_cpu_topology(unsigned int cpuid);
 | 
			
		||||
const struct cpumask *cpu_coregroup_mask(int cpu);
 | 
			
		||||
void update_siblings_masks(unsigned int cpu);
 | 
			
		||||
void remove_cpu_topology(unsigned int cpuid);
 | 
			
		||||
void reset_cpu_topology(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_ARCH_TOPOLOGY_H_ */
 | 
			
		||||
 | 
			
		||||
@ -256,7 +256,10 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov)
 | 
			
		||||
{return 0;}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, is_retention) \
 | 
			
		||||
#define __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter,			\
 | 
			
		||||
				idx,					\
 | 
			
		||||
				state,					\
 | 
			
		||||
				is_retention)				\
 | 
			
		||||
({									\
 | 
			
		||||
	int __ret = 0;							\
 | 
			
		||||
									\
 | 
			
		||||
@ -268,7 +271,7 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov)
 | 
			
		||||
	if (!is_retention)						\
 | 
			
		||||
		__ret =  cpu_pm_enter();				\
 | 
			
		||||
	if (!__ret) {							\
 | 
			
		||||
		__ret = low_level_idle_enter(idx);			\
 | 
			
		||||
		__ret = low_level_idle_enter(state);			\
 | 
			
		||||
		if (!is_retention)					\
 | 
			
		||||
			cpu_pm_exit();					\
 | 
			
		||||
	}								\
 | 
			
		||||
@ -277,9 +280,15 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
#define CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx)	\
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, 0)
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, idx, 0)
 | 
			
		||||
 | 
			
		||||
#define CPU_PM_CPU_IDLE_ENTER_RETENTION(low_level_idle_enter, idx)	\
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, 1)
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, idx, 1)
 | 
			
		||||
 | 
			
		||||
#define CPU_PM_CPU_IDLE_ENTER_PARAM(low_level_idle_enter, idx, state)	\
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, state, 0)
 | 
			
		||||
 | 
			
		||||
#define CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(low_level_idle_enter, idx, state)	\
 | 
			
		||||
	__CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, state, 1)
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_CPUIDLE_H */
 | 
			
		||||
 | 
			
		||||
@ -2,16 +2,16 @@
 | 
			
		||||
#ifndef _LINUX_ERROR_INJECTION_H
 | 
			
		||||
#define _LINUX_ERROR_INJECTION_H
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
			
		||||
#include <linux/compiler.h>
 | 
			
		||||
#include <asm-generic/error-injection.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/error-injection.h>
 | 
			
		||||
#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
			
		||||
 | 
			
		||||
extern bool within_error_injection_list(unsigned long addr);
 | 
			
		||||
extern int get_injectable_error_type(unsigned long addr);
 | 
			
		||||
 | 
			
		||||
#else /* !CONFIG_FUNCTION_ERROR_INJECTION */
 | 
			
		||||
 | 
			
		||||
#include <asm-generic/error-injection.h>
 | 
			
		||||
static inline bool within_error_injection_list(unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	return false;
 | 
			
		||||
 | 
			
		||||
@ -15,8 +15,8 @@
 | 
			
		||||
 | 
			
		||||
bool psci_tos_resident_on(int cpu);
 | 
			
		||||
 | 
			
		||||
int psci_cpu_init_idle(unsigned int cpu);
 | 
			
		||||
int psci_cpu_suspend_enter(unsigned long index);
 | 
			
		||||
int psci_cpu_suspend_enter(u32 state);
 | 
			
		||||
bool psci_power_state_is_valid(u32 state);
 | 
			
		||||
 | 
			
		||||
enum psci_conduit {
 | 
			
		||||
	PSCI_CONDUIT_NONE,
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ struct random_ready_callback {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern void add_device_randomness(const void *, unsigned int);
 | 
			
		||||
extern void add_bootloader_randomness(const void *, unsigned int);
 | 
			
		||||
 | 
			
		||||
#if defined(LATENT_ENTROPY_PLUGIN) && !defined(__CHECKER__)
 | 
			
		||||
static inline void add_latent_entropy(void)
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,7 @@
 | 
			
		||||
#ifndef _LINUX_TOPOLOGY_H
 | 
			
		||||
#define _LINUX_TOPOLOGY_H
 | 
			
		||||
 | 
			
		||||
#include <linux/arch_topology.h>
 | 
			
		||||
#include <linux/cpumask.h>
 | 
			
		||||
#include <linux/bitops.h>
 | 
			
		||||
#include <linux/mmzone.h>
 | 
			
		||||
 | 
			
		||||
@ -229,4 +229,9 @@ struct prctl_mm_map {
 | 
			
		||||
# define PR_PAC_APDBKEY			(1UL << 3)
 | 
			
		||||
# define PR_PAC_APGAKEY			(1UL << 4)
 | 
			
		||||
 | 
			
		||||
/* Tagged user address controls for arm64 */
 | 
			
		||||
#define PR_SET_TAGGED_ADDR_CTRL		55
 | 
			
		||||
#define PR_GET_TAGGED_ADDR_CTRL		56
 | 
			
		||||
# define PR_TAGGED_ADDR_ENABLE		(1UL << 0)
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_PRCTL_H */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								kernel/sys.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								kernel/sys.c
									
									
									
									
									
								
							@ -124,6 +124,12 @@
 | 
			
		||||
#ifndef PAC_RESET_KEYS
 | 
			
		||||
# define PAC_RESET_KEYS(a, b)	(-EINVAL)
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef SET_TAGGED_ADDR_CTRL
 | 
			
		||||
# define SET_TAGGED_ADDR_CTRL(a)	(-EINVAL)
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef GET_TAGGED_ADDR_CTRL
 | 
			
		||||
# define GET_TAGGED_ADDR_CTRL()		(-EINVAL)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * this is where the system-wide overflow UID and GID are defined, for
 | 
			
		||||
@ -2492,6 +2498,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		error = PAC_RESET_KEYS(me, arg2);
 | 
			
		||||
		break;
 | 
			
		||||
	case PR_SET_TAGGED_ADDR_CTRL:
 | 
			
		||||
		if (arg3 || arg4 || arg5)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		error = SET_TAGGED_ADDR_CTRL(arg2);
 | 
			
		||||
		break;
 | 
			
		||||
	case PR_GET_TAGGED_ADDR_CTRL:
 | 
			
		||||
		if (arg2 || arg3 || arg4 || arg5)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		error = GET_TAGGED_ADDR_CTRL();
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		error = -EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,9 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
ifdef CONFIG_KASAN
 | 
			
		||||
CFLAGS_KASAN_NOSANITIZE := -fno-builtin
 | 
			
		||||
KASAN_SHADOW_OFFSET ?= $(CONFIG_KASAN_SHADOW_OFFSET)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_KASAN_GENERIC
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_KASAN_INLINE
 | 
			
		||||
@ -7,8 +12,6 @@ else
 | 
			
		||||
	call_threshold := 0
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
KASAN_SHADOW_OFFSET ?= $(CONFIG_KASAN_SHADOW_OFFSET)
 | 
			
		||||
 | 
			
		||||
CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
 | 
			
		||||
 | 
			
		||||
cc-param = $(call cc-option, -mllvm -$(1), $(call cc-option, --param $(1)))
 | 
			
		||||
@ -45,7 +48,3 @@ CFLAGS_KASAN := -fsanitize=kernel-hwaddress \
 | 
			
		||||
		$(instrumentation_flags)
 | 
			
		||||
 | 
			
		||||
endif # CONFIG_KASAN_SW_TAGS
 | 
			
		||||
 | 
			
		||||
ifdef CONFIG_KASAN
 | 
			
		||||
CFLAGS_KASAN_NOSANITIZE := -fno-builtin
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								tools/testing/selftests/arm64/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tools/testing/selftests/arm64/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
tags_test
 | 
			
		||||
							
								
								
									
										11
									
								
								tools/testing/selftests/arm64/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tools/testing/selftests/arm64/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
# ARCH can be overridden by the user for cross compiling
 | 
			
		||||
ARCH ?= $(shell uname -m 2>/dev/null || echo not)
 | 
			
		||||
 | 
			
		||||
ifneq (,$(filter $(ARCH),aarch64 arm64))
 | 
			
		||||
TEST_GEN_PROGS := tags_test
 | 
			
		||||
TEST_PROGS := run_tags_test.sh
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
include ../lib.mk
 | 
			
		||||
							
								
								
									
										12
									
								
								tools/testing/selftests/arm64/run_tags_test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								tools/testing/selftests/arm64/run_tags_test.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
echo "--------------------"
 | 
			
		||||
echo "running tags test"
 | 
			
		||||
echo "--------------------"
 | 
			
		||||
./tags_test
 | 
			
		||||
if [ $? -ne 0 ]; then
 | 
			
		||||
	echo "[FAIL]"
 | 
			
		||||
else
 | 
			
		||||
	echo "[PASS]"
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										31
									
								
								tools/testing/selftests/arm64/tags_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tools/testing/selftests/arm64/tags_test.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/prctl.h>
 | 
			
		||||
#include <sys/utsname.h>
 | 
			
		||||
 | 
			
		||||
#define SHIFT_TAG(tag)		((uint64_t)(tag) << 56)
 | 
			
		||||
#define SET_TAG(ptr, tag)	(((uint64_t)(ptr) & ~SHIFT_TAG(0xff)) | \
 | 
			
		||||
					SHIFT_TAG(tag))
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	static int tbi_enabled = 0;
 | 
			
		||||
	unsigned long tag = 0;
 | 
			
		||||
	struct utsname *ptr;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0)
 | 
			
		||||
		tbi_enabled = 1;
 | 
			
		||||
	ptr = (struct utsname *)malloc(sizeof(*ptr));
 | 
			
		||||
	if (tbi_enabled)
 | 
			
		||||
		tag = 0x42;
 | 
			
		||||
	ptr = (struct utsname *)SET_TAG(ptr, tag);
 | 
			
		||||
	err = uname(ptr);
 | 
			
		||||
	free(ptr);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user