LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] powerpc: mm: Limit rma_size to 1TB when running without HV mode
From: Suraj Jitindar Singh @ 2019-07-10  5:20 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: sjitindarsingh, kvm-ppc, david

The virtual real mode addressing (VRMA) mechanism is used when a
partition is using HPT (Hash Page Table) translation and performs
real mode accesses (MSR[IR|DR] = 0) in non-hypervisor mode. In this
mode effective address bits 0:23 are treated as zero (i.e. the access
is aliased to 0) and the access is performed using an implicit 1TB SLB
entry.

The size of the RMA (Real Memory Area) is communicated to the guest as
the size of the first memory region in the device tree. And because of
the mechanism described above can be expected to not exceed 1TB. In the
event that the host erroneously represents the RMA as being larger than
1TB, guest accesses in real mode to memory addresses above 1TB will be
aliased down to below 1TB. This means that a memory access performed in
real mode may differ to one performed in virtual mode for the same memory
address, which would likely have unintended consequences.

To avoid this outcome have the guest explicitly limit the size of the
RMA to the current maximum, which is 1TB. This means that even if the
first memory block is larger than 1TB, only the first 1TB should be
accessed in real mode.

Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
---
 arch/powerpc/mm/book3s64/hash_utils.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c
index 28ced26f2a00..4d0e2cce9cd5 100644
--- a/arch/powerpc/mm/book3s64/hash_utils.c
+++ b/arch/powerpc/mm/book3s64/hash_utils.c
@@ -1901,11 +1901,19 @@ void hash__setup_initial_memory_limit(phys_addr_t first_memblock_base,
 	 *
 	 * For guests on platforms before POWER9, we clamp the it limit to 1G
 	 * to avoid some funky things such as RTAS bugs etc...
+	 * On POWER9 we limit to 1TB in case the host erroneously told us that
+	 * the RMA was >1TB. Effective address bits 0:23 are treated as zero
+	 * (meaning the access is aliased to zero i.e. addr = addr % 1TB)
+	 * for virtual real mode addressing and so it doesn't make sense to
+	 * have an area larger than 1TB as it can't be addressed.
 	 */
 	if (!early_cpu_has_feature(CPU_FTR_HVMODE)) {
 		ppc64_rma_size = first_memblock_size;
 		if (!early_cpu_has_feature(CPU_FTR_ARCH_300))
 			ppc64_rma_size = min_t(u64, ppc64_rma_size, 0x40000000);
+		else
+			ppc64_rma_size = min_t(u64, ppc64_rma_size,
+					       1UL << SID_SHIFT_1T);
 
 		/* Finally limit subsequent allocations */
 		memblock_set_current_limit(ppc64_rma_size);
-- 
2.13.6


^ permalink raw reply related

* Re: [PATCH 3/5] x86: Kconfig: Remove CONFIG_NODES_SPAN_OTHER_NODES
From: Thomas Gleixner @ 2019-07-10  5:58 UTC (permalink / raw)
  To: Hoan Tran OS
  Cc: Michal Hocko, Catalin Marinas, Heiko Carstens,
	open list:MEMORY MANAGEMENT, Paul Mackerras, H . Peter Anvin,
	sparclinux@vger.kernel.org, Alexander Duyck,
	linux-s390@vger.kernel.org, x86@kernel.org, Mike Rapoport,
	Christian Borntraeger, Ingo Molnar, Vlastimil Babka,
	Open Source Submission, Pavel Tatashin, Vasily Gorbik,
	Will Deacon, Borislav Petkov,
	linux-arm-kernel@lists.infradead.org, Oscar Salvador,
	linux-kernel@vger.kernel.org, Andrew Morton,
	linuxppc-dev@lists.ozlabs.org, David S . Miller
In-Reply-To: <1c5bc3a8-0c6f-dce3-95a2-8aec765408a2@os.amperecomputing.com>

Hoan,

On Wed, 10 Jul 2019, Hoan Tran OS wrote:
> On 6/25/19 3:45 PM, Thomas Gleixner wrote:
> > On Tue, 25 Jun 2019, Hoan Tran OS wrote:
> >> @@ -1567,15 +1567,6 @@ config X86_64_ACPI_NUMA
> >>   	---help---
> >>   	  Enable ACPI SRAT based node topology detection.
> >>   
> >> -# Some NUMA nodes have memory ranges that span
> >> -# other nodes.  Even though a pfn is valid and
> >> -# between a node's start and end pfns, it may not
> >> -# reside on that node.  See memmap_init_zone()
> >> -# for details.
> >> -config NODES_SPAN_OTHER_NODES
> >> -	def_bool y
> >> -	depends on X86_64_ACPI_NUMA
> > 
> > the changelog does not mention that this lifts the dependency on
> > X86_64_ACPI_NUMA and therefore enables that functionality for anything
> > which has NUMA enabled including 32bit.
> > 
> 
> I think this config is used for a NUMA layout which NUMA nodes addresses 
> are spanned to other nodes. I think 32bit NUMA also have the same issue 
> with that layout. Please correct me if I'm wrong.

I'm not saying you're wrong, but it's your duty to provide the analysis why
this is correct for everything which has NUMA enabled.

> > The core mm change gives no helpful information either. You just copied the
> > above comment text from some random Kconfig.
> 
> Yes, as it's a correct comment and is used at multiple places.

Well it maybe correct in terms of explaining what this is about, it still
does not explain why this is needed by default on everything which has NUMA
enabled.

Thanks,

	tglx

^ permalink raw reply

* Re: [PATCH 3/5] x86: Kconfig: Remove CONFIG_NODES_SPAN_OTHER_NODES
From: Hoan Tran OS @ 2019-07-10  6:14 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Michal Hocko, Catalin Marinas, Heiko Carstens,
	open list:MEMORY MANAGEMENT, Paul Mackerras, H . Peter Anvin,
	sparclinux@vger.kernel.org, Alexander Duyck,
	linux-s390@vger.kernel.org, x86@kernel.org, Mike Rapoport,
	Christian Borntraeger, Ingo Molnar, Vlastimil Babka,
	Open Source Submission, Pavel Tatashin, Vasily Gorbik,
	Will Deacon, Borislav Petkov,
	linux-arm-kernel@lists.infradead.org, Oscar Salvador,
	linux-kernel@vger.kernel.org, Andrew Morton,
	linuxppc-dev@lists.ozlabs.org, David S . Miller
In-Reply-To: <alpine.DEB.2.21.1907100755010.1758@nanos.tec.linutronix.de>

Hi Thomas,


On 7/10/19 12:58 PM, Thomas Gleixner wrote:
> Hoan,
> 
> On Wed, 10 Jul 2019, Hoan Tran OS wrote:
>> On 6/25/19 3:45 PM, Thomas Gleixner wrote:
>>> On Tue, 25 Jun 2019, Hoan Tran OS wrote:
>>>> @@ -1567,15 +1567,6 @@ config X86_64_ACPI_NUMA
>>>>    	---help---
>>>>    	  Enable ACPI SRAT based node topology detection.
>>>>    
>>>> -# Some NUMA nodes have memory ranges that span
>>>> -# other nodes.  Even though a pfn is valid and
>>>> -# between a node's start and end pfns, it may not
>>>> -# reside on that node.  See memmap_init_zone()
>>>> -# for details.
>>>> -config NODES_SPAN_OTHER_NODES
>>>> -	def_bool y
>>>> -	depends on X86_64_ACPI_NUMA
>>>
>>> the changelog does not mention that this lifts the dependency on
>>> X86_64_ACPI_NUMA and therefore enables that functionality for anything
>>> which has NUMA enabled including 32bit.
>>>
>>
>> I think this config is used for a NUMA layout which NUMA nodes addresses
>> are spanned to other nodes. I think 32bit NUMA also have the same issue
>> with that layout. Please correct me if I'm wrong.
> 
> I'm not saying you're wrong, but it's your duty to provide the analysis why
> this is correct for everything which has NUMA enabled.
> 
>>> The core mm change gives no helpful information either. You just copied the
>>> above comment text from some random Kconfig.
>>
>> Yes, as it's a correct comment and is used at multiple places.
> 
> Well it maybe correct in terms of explaining what this is about, it still
> does not explain why this is needed by default on everything which has NUMA
> enabled.

Let me send another patch with the detail explanation.

Thanks
Hoan

> 
> Thanks,
> 
> 	tglx
> 

^ permalink raw reply

* Re: [PATCH v3 2/3] Powerpc64/Watchpoint: Don't ignore extraneous exceptions
From: Christophe Leroy @ 2019-07-10  6:27 UTC (permalink / raw)
  To: Ravi Bangoria, mpe, mikey
  Cc: linux-kernel, npiggin, paulus, naveen.n.rao, linuxppc-dev
In-Reply-To: <20190710045445.31037-3-ravi.bangoria@linux.ibm.com>



Le 10/07/2019 à 06:54, Ravi Bangoria a écrit :
> On Powerpc64, watchpoint match range is double-word granular. On
> a watchpoint hit, DAR is set to the first byte of overlap between
> actual access and watched range. And thus it's quite possible that
> DAR does not point inside user specified range. Ex, say user creates
> a watchpoint with address range 0x1004 to 0x1007. So hw would be
> configured to watch from 0x1000 to 0x1007. If there is a 4 byte
> access from 0x1002 to 0x1005, DAR will point to 0x1002 and thus
> interrupt handler considers it as extraneous, but it's actually not,
> because part of the access belongs to what user has asked. So, let
> kernel pass it on to user and let user decide what to do with it
> instead of silently ignoring it. The drawback is, it can generate
> false positive events.

Why adding some #ifdefs based on CONFIG_8xx ?

I see your commit log mentions 'Powerpc64'. What about BOOK3S/32 ?

Christophe

> 
> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
> ---
>   arch/powerpc/kernel/hw_breakpoint.c | 9 +++++----
>   1 file changed, 5 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
> index 5c876e986c18..c457d52778e3 100644
> --- a/arch/powerpc/kernel/hw_breakpoint.c
> +++ b/arch/powerpc/kernel/hw_breakpoint.c
> @@ -204,9 +204,10 @@ int hw_breakpoint_handler(struct die_args *args)
>   #ifndef CONFIG_PPC_8xx
>   	int stepped = 1;
>   	unsigned int instr;
> +#else
> +	unsigned long dar = regs->dar;
>   #endif
>   	struct arch_hw_breakpoint *info;
> -	unsigned long dar = regs->dar;
>   
>   	/* Disable breakpoints during exception handling */
>   	hw_breakpoint_disable();
> @@ -240,14 +241,14 @@ int hw_breakpoint_handler(struct die_args *args)
>   
>   	/*
>   	 * Verify if dar lies within the address range occupied by the symbol
> -	 * being watched to filter extraneous exceptions.  If it doesn't,
> -	 * we still need to single-step the instruction, but we don't
> -	 * generate an event.
> +	 * being watched to filter extraneous exceptions.
>   	 */
>   	info->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ;
> +#ifdef CONFIG_PPC_8xx
>   	if (!((bp->attr.bp_addr <= dar) &&
>   	      (dar - bp->attr.bp_addr < bp->attr.bp_len)))
>   		info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
> +#endif
>   
>   #ifndef CONFIG_PPC_8xx
>   	/* Do not emulate user-space instructions, instead single-step them */
> 

^ permalink raw reply

* Re: [PATCH v3 2/3] Powerpc64/Watchpoint: Don't ignore extraneous exceptions
From: Ravi Bangoria @ 2019-07-10  6:38 UTC (permalink / raw)
  To: Christophe Leroy
  Cc: Ravi Bangoria, mikey, linux-kernel, npiggin, paulus, naveen.n.rao,
	linuxppc-dev
In-Reply-To: <1f3fd425-3d2f-8d18-eff1-01ca5b605ba0@c-s.fr>


On 7/10/19 11:57 AM, Christophe Leroy wrote:
> 
> 
> Le 10/07/2019 à 06:54, Ravi Bangoria a écrit :
>> On Powerpc64, watchpoint match range is double-word granular. On
>> a watchpoint hit, DAR is set to the first byte of overlap between
>> actual access and watched range. And thus it's quite possible that
>> DAR does not point inside user specified range. Ex, say user creates
>> a watchpoint with address range 0x1004 to 0x1007. So hw would be
>> configured to watch from 0x1000 to 0x1007. If there is a 4 byte
>> access from 0x1002 to 0x1005, DAR will point to 0x1002 and thus
>> interrupt handler considers it as extraneous, but it's actually not,
>> because part of the access belongs to what user has asked. So, let
>> kernel pass it on to user and let user decide what to do with it
>> instead of silently ignoring it. The drawback is, it can generate
>> false positive events.
> 
> Why adding some #ifdefs based on CONFIG_8xx ?

I don't know how 8xx behaves so I'm keeping the current behavior(ignore
extraneous exception) for 8xx.

> 
> I see your commit log mentions 'Powerpc64'. What about BOOK3S/32 ?

Hmm, I should not have mention 64 there. Yes, the change should cover both
Books3S/64 and Book3S/32.


^ permalink raw reply

* Re: [1/2] powerpc/83xx: fix use-after-free in mpc831x_usb_cfg()
From: Markus Elfring @ 2019-07-10  7:19 UTC (permalink / raw)
  To: Wen Yang, linuxppc-dev
  Cc: Yi Wang, Cheng Shengyu, kernel-janitors, linux-kernel, Scott Wood,
	Markus.Elfring, Xue Zhihong, Paul Mackerras
In-Reply-To: <1562670768-23178-2-git-send-email-wen.yang99@zte.com.cn>

> The immr_node variable is still being used after the of_node_put() call,
> which may result in use-after-free.

Was any known source code analysis tool involved to point such
a questionable implementation detail out for further software
development considerations?

Regards,
Markus

^ permalink raw reply

* Re: [PATCH] powerpc: mm: Limit rma_size to 1TB when running without HV mode
From: Satheesh Rajendran @ 2019-07-10  7:32 UTC (permalink / raw)
  To: Suraj Jitindar Singh; +Cc: linuxppc-dev, kvm-ppc, david
In-Reply-To: <20190710052018.14628-1-sjitindarsingh@gmail.com>

On Wed, Jul 10, 2019 at 03:20:18PM +1000, Suraj Jitindar Singh wrote:
> The virtual real mode addressing (VRMA) mechanism is used when a
> partition is using HPT (Hash Page Table) translation and performs
> real mode accesses (MSR[IR|DR] = 0) in non-hypervisor mode. In this
> mode effective address bits 0:23 are treated as zero (i.e. the access
> is aliased to 0) and the access is performed using an implicit 1TB SLB
> entry.
> 
> The size of the RMA (Real Memory Area) is communicated to the guest as
> the size of the first memory region in the device tree. And because of
> the mechanism described above can be expected to not exceed 1TB. In the
> event that the host erroneously represents the RMA as being larger than
> 1TB, guest accesses in real mode to memory addresses above 1TB will be
> aliased down to below 1TB. This means that a memory access performed in
> real mode may differ to one performed in virtual mode for the same memory
> address, which would likely have unintended consequences.
> 
> To avoid this outcome have the guest explicitly limit the size of the
> RMA to the current maximum, which is 1TB. This means that even if the
> first memory block is larger than 1TB, only the first 1TB should be
> accessed in real mode.
> 
> Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
> ---
>  arch/powerpc/mm/book3s64/hash_utils.c | 8 ++++++++
>  1 file changed, 8 insertions(+)

Hi,

Tested this patch and now Power8 compat guest boots fine with mem >1024G on 
Power9 host.

Tested-by: Satheesh Rajendran <sathnaga@linux.vnet.ibm.com>

Host: P9; kernel: 5.2.0-00915-g5ad18b2e60b7

Before this patch:
Guest crashes..
[0.000000] BUG: Kernel NULL pointer dereference at 0x00000028
[0.000000] Faulting instruction address: 0xc00000000102caa0
[0.000000] Oops: Kernel access of bad area, sig: 11 [#1]
[0.000000] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA pSeries
[0.000000] Modules linked in:
[0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 5.2.0-03135-ge9a83bd23220 #24
[0.000000] NIP:  c00000000102caa0 LR: c00000000102ca84 CTR: 0000000000000000
[0.000000] REGS: c000000001603ba0 TRAP: 0380   Not tainted  (5.2.0-03135-ge9a83bd23220)
[0.000000] MSR:  8000000000001033 <SF,ME,IR,DR,RI,LE>  CR: 24000428  XER: 20000000
[0.000000] CFAR: c00000000102c1d8 IRQMASK: 1 
[0.000000] GPR00: c00000000102ca84 c000000001603e30 c000000001605300 0000010000000000 
[0.000000] GPR04: 0000000000000000 0000000000000000 c00000ffffff8000 c000000001863dc8 
[0.000000] GPR08: 0000000000002028 0000000000000000 c00000ffffff8000 0000000000000009 
[0.000000] GPR12: 0000000000000000 c0000000018f0000 000000007dc5fef0 00000000012e1220 
[0.000000] GPR16: 00000000012e10a0 fffffffffffffffd 000000007dc5fef0 000000000130fcc0 
[0.000000] GPR20: 0000000000000014 0000000001a80000 000000002fff0000 fffffffffffffffd 
[0.000000] GPR24: 0000000001d0000c c000000000000000 c000000001641ed8 c000000001641b78 
[0.000000] GPR28: 0000000000000000 0000000000000000 0000010000000000 0000000000000000 
[0.000000] NIP [c00000000102caa0] emergency_stack_init+0xb8/0x118
[0.000000] LR [c00000000102ca84] emergency_stack_init+0x9c/0x118
[0.000000] Call Trace:
[0.000000] [c000000001603e30] [c00000000102ca84] emergency_stack_init+0x9c/0x118 (unreliable)
[0.000000] [c000000001603e80] [c00000000102bd54] setup_arch+0x2fc/0x388
[0.000000] [c000000001603ef0] [c000000001023ccc] start_kernel+0xa4/0x660
[0.000000] [c000000001603f90] [c00000000000b774] start_here_common+0x1c/0x528
[0.000000] Instruction dump:
[0.000000] 7ffc07b4 7fc3f378 7bfd1f24 7f84e378 4bfff6e9 3f620004 3b7bc878 7f84e378 
[0.000000] 39434000 7fc3f378 e93b0000 7d29e82a <f9490028> 4bfff6c5 e93b0000 7f84e378 
[0.000000] random: get_random_bytes called from print_oops_end_marker+0x6c/0xa0 with crng_init=0
[0.000000] ---[ end trace 0000000000000000 ]---
[0.000000] 
[0.000000] Kernel panic - not syncing: Attempted to kill the idle task!

-------------------------
With this patch:
# virsh start --console p8
Domain p8 started
Connected to domain p8
..
..
Fedora 27 (Twenty Seven)
Kernel 5.2.0-03136-gf709b0494ad9 on an ppc64le (hvc0)

atest-guest login: 
# free -g
              total        used        free      shared  buff/cache   available
Mem:           1028       0        1027           0           0        1025
Swap:         0           0     

Regards,
-Satheesh.

> 
> diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c
> index 28ced26f2a00..4d0e2cce9cd5 100644
> --- a/arch/powerpc/mm/book3s64/hash_utils.c
> +++ b/arch/powerpc/mm/book3s64/hash_utils.c
> @@ -1901,11 +1901,19 @@ void hash__setup_initial_memory_limit(phys_addr_t first_memblock_base,
>  	 *
>  	 * For guests on platforms before POWER9, we clamp the it limit to 1G
>  	 * to avoid some funky things such as RTAS bugs etc...
> +	 * On POWER9 we limit to 1TB in case the host erroneously told us that
> +	 * the RMA was >1TB. Effective address bits 0:23 are treated as zero
> +	 * (meaning the access is aliased to zero i.e. addr = addr % 1TB)
> +	 * for virtual real mode addressing and so it doesn't make sense to
> +	 * have an area larger than 1TB as it can't be addressed.
>  	 */
>  	if (!early_cpu_has_feature(CPU_FTR_HVMODE)) {
>  		ppc64_rma_size = first_memblock_size;
>  		if (!early_cpu_has_feature(CPU_FTR_ARCH_300))
>  			ppc64_rma_size = min_t(u64, ppc64_rma_size, 0x40000000);
> +		else
> +			ppc64_rma_size = min_t(u64, ppc64_rma_size,
> +					       1UL << SID_SHIFT_1T);
> 
>  		/* Finally limit subsequent allocations */
>  		memblock_set_current_limit(ppc64_rma_size);
> -- 
> 2.13.6
> 


^ permalink raw reply

* Re: [1/2] powerpc/83xx: fix use-after-free in mpc831x_usb_cfg()
From: wen.yang99 @ 2019-07-10  7:33 UTC (permalink / raw)
  To: Markus.Elfring
  Cc: wang.yi59, kernel-janitors, linux-kernel, oss, Markus.Elfring,
	xue.zhihong, cheng.shengyu, paulus, linuxppc-dev
In-Reply-To: <3138c2fa-1e99-f672-2108-33dd1cf25ca2@web.de>


[-- Attachment #1.1: Type: text/plain, Size: 479 bytes --]

> > The immr_node variable is still being used after the of_node_put() call,
> > which may result in use-after-free.
> 
> Was any known source code analysis tool involved to point such
> a questionable implementation detail out for further software
> development considerations?

Hi Markus, 
we developed a coccinelle script to detect such problems. 
This script is still being improved.
After a period of testing, we will send it to the LMKL mailing list later.

--
Regards,
Wen

^ permalink raw reply

* [PATCH v3] cpufreq/pasemi: fix an use-after-free inpas_cpufreq_cpu_init()
From: Christian Zigotzky @ 2019-07-10  7:55 UTC (permalink / raw)
  To: wen.yang99, linuxppc-dev
In-Reply-To: <201907090939164296374@zte.com.cn>

Is a final patch available for testing? Please do not release it without 
testing.

- Christian

On 09-07-19, 16:04, Wen Yang wrote:
 > The cpu variable is still being used in the of_get_property() call
 > after the of_node_put() call, which may result in use-after-free.
 >
 > Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
 > Signed-off-by: Wen Yang <wen.yang99 at zte.com.cn>
 > Cc: "Rafael J. Wysocki" <rjw at rjwysocki.net>
 > Cc: Viresh Kumar <viresh.kumar at linaro.org>
 > Cc: Michael Ellerman <mpe at ellerman.id.au>
 > Cc: linuxppc-dev at lists.ozlabs.org
 > Cc: linux-pm at vger.kernel.org
 > Cc: linux-kernel at vger.kernel.org
 > ---
 > v5: put together the code to get, use, and release cpu device_node.
 > v4: restore the blank line.
 > v3: fix a leaked reference.
 > v2: clean up the code according to the advice of viresh.
 >
 >  drivers/cpufreq/pasemi-cpufreq.c | 21 +++++++++------------
 >  1 file changed, 9 insertions(+), 12 deletions(-)
 >
 > diff --git a/drivers/cpufreq/pasemi-cpufreq.c 
b/drivers/cpufreq/pasemi-cpufreq.c
 > index 6b1e4ab..1f0beb7 100644
 > --- a/drivers/cpufreq/pasemi-cpufreq.c
 > +++ b/drivers/cpufreq/pasemi-cpufreq.c
 > @@ -131,10 +131,17 @@ static int pas_cpufreq_cpu_init(struct 
cpufreq_policy *policy)
 >      int err = -ENODEV;
 >
 >      cpu = of_get_cpu_node(policy->cpu, NULL);
 > -
 > -    of_node_put(cpu);
 >      if (!cpu)
 >          goto out;

I would have loved a blank line here :)

 > +    max_freqp = of_get_property(cpu, "clock-frequency", NULL);
 > +    of_node_put(cpu);
 > +    if (!max_freqp) {
 > +        err = -EINVAL;
 > +        goto out;
 > +    }
 > +
 > +    /* we need the freq in kHz */
 > +    max_freq = *max_freqp / 1000;
 >
 >      dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
 >      if (!dn)
 > @@ -171,16 +178,6 @@ static int pas_cpufreq_cpu_init(struct 
cpufreq_policy *policy)
 >      }
 >
 >      pr_debug("init cpufreq on CPU %d\n", policy->cpu);
 > -
 > -    max_freqp = of_get_property(cpu, "clock-frequency", NULL);
 > -    if (!max_freqp) {
 > -        err = -EINVAL;
 > -        goto out_unmap_sdcpwr;
 > -    }
 > -
 > -    /* we need the freq in kHz */
 > -    max_freq = *max_freqp / 1000;
 > -
 >      pr_debug("max clock-frequency is at %u kHz\n", max_freq);
 >      pr_debug("initializing frequency table\n");

Though, enough versions have happened now.

Acked-by: Viresh Kumar <viresh.kumar at linaro.org>

-- 
viresh

^ permalink raw reply

* Re: [1/2] powerpc/83xx: fix use-after-free in mpc831x_usb_cfg()
From: Markus Elfring @ 2019-07-10  9:24 UTC (permalink / raw)
  To: Wen Yang, linuxppc-dev
  Cc: Yi Wang, Cheng Shengyu, kernel-janitors, linux-kernel, Scott Wood,
	Paul Mackerras, Xue Zhihong
In-Reply-To: <201907101533443009168@zte.com.cn>

> we developed a coccinelle script to detect such problems.

How do you think about to give any attribution to this development software
in your commit descriptions?


> After a period of testing, we will send it to the LMKL mailing list later.

I am also curious then on how this area will evolve further.

Regards,
Markus

^ permalink raw reply

* Re: [PATCH] sound: ppc: snd_ps3: Remove Unneeded variable: "ret"
From: Takashi Iwai @ 2019-07-10  9:56 UTC (permalink / raw)
  To: Hariprasad Kelam
  Cc: alsa-devel, Geoff Levand, Takashi Iwai, Jaroslav Kysela,
	Paul Mackerras, linuxppc-dev, linux-kernel
In-Reply-To: <20190710023946.GA15604@hari-Inspiron-1545>

On Wed, 10 Jul 2019 04:39:46 +0200,
Hariprasad Kelam wrote:
> 
> This patch fixes below issue reported by coccicheck
> sound/ppc/snd_ps3.c:631:5-8: Unneeded variable: "ret". Return "0" on
> line 668
> 
> We cannot change return type of snd_ps3_pcm_trigger as it is registered
> with snd_pcm_ops->trigger
> 
> Signed-off-by: Hariprasad Kelam <hariprasad.kelam@gmail.com>

Applied (with the correction of the description again).


thanks,

Takashi

^ permalink raw reply

* Re: [PATCH v5] cpufreq/pasemi: fix an use-after-free in pas_cpufreq_cpu_init()
From: Rafael J. Wysocki @ 2019-07-10 10:07 UTC (permalink / raw)
  To: Viresh Kumar, Wen Yang
  Cc: wang.yi59, linux-pm, linux-kernel, xue.zhihong, cheng.shengyu,
	linuxppc-dev
In-Reply-To: <20190709081205.a5sjsqo2el6zt24b@vireshk-i7>

On Tuesday, July 9, 2019 10:12:05 AM CEST Viresh Kumar wrote:
> On 09-07-19, 16:04, Wen Yang wrote:
> > The cpu variable is still being used in the of_get_property() call
> > after the of_node_put() call, which may result in use-after-free.
> > 
> > Fixes: a9acc26b75f ("cpufreq/pasemi: fix possible object reference leak")
> > Signed-off-by: Wen Yang <wen.yang99@zte.com.cn>
> > Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> > Cc: Viresh Kumar <viresh.kumar@linaro.org>
> > Cc: Michael Ellerman <mpe@ellerman.id.au>
> > Cc: linuxppc-dev@lists.ozlabs.org
> > Cc: linux-pm@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org

Patch applied.

> > ---
> > v5: put together the code to get, use, and release cpu device_node.
> > v4: restore the blank line.
> > v3: fix a leaked reference.
> > v2: clean up the code according to the advice of viresh.
> > 
> >  drivers/cpufreq/pasemi-cpufreq.c | 21 +++++++++------------
> >  1 file changed, 9 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
> > index 6b1e4ab..1f0beb7 100644
> > --- a/drivers/cpufreq/pasemi-cpufreq.c
> > +++ b/drivers/cpufreq/pasemi-cpufreq.c
> > @@ -131,10 +131,17 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> >  	int err = -ENODEV;
> >  
> >  	cpu = of_get_cpu_node(policy->cpu, NULL);
> > -
> > -	of_node_put(cpu);
> >  	if (!cpu)
> >  		goto out;
> 
> I would have loved a blank line here :)

And I added the blank line.

> > +	max_freqp = of_get_property(cpu, "clock-frequency", NULL);
> > +	of_node_put(cpu);
> > +	if (!max_freqp) {
> > +		err = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	/* we need the freq in kHz */
> > +	max_freq = *max_freqp / 1000;
> >  
> >  	dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
> >  	if (!dn)
> > @@ -171,16 +178,6 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
> >  	}
> >  
> >  	pr_debug("init cpufreq on CPU %d\n", policy->cpu);
> > -
> > -	max_freqp = of_get_property(cpu, "clock-frequency", NULL);
> > -	if (!max_freqp) {
> > -		err = -EINVAL;
> > -		goto out_unmap_sdcpwr;
> > -	}
> > -
> > -	/* we need the freq in kHz */
> > -	max_freq = *max_freqp / 1000;
> > -
> >  	pr_debug("max clock-frequency is at %u kHz\n", max_freq);
> >  	pr_debug("initializing frequency table\n");
> 
> Though, enough versions have happened now.
> 
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> 
> 

Thanks!




^ permalink raw reply

* Re: [PATCH v5 7/7] KVM: PPC: Ultravisor: Add PPC_UV config option
From: janani @ 2019-07-10 13:24 UTC (permalink / raw)
  To: Bharata B Rao
  Cc: linuxram, cclaudio, kvm-ppc, Linuxppc-dev, linux-mm, jglisse,
	aneesh.kumar, paulus, sukadev, linuxppc-dev, Anshuman Khandual
In-Reply-To: <20190709102545.9187-8-bharata@linux.ibm.com>

On 2019-07-09 05:25, Bharata B Rao wrote:
> From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> 
> CONFIG_PPC_UV adds support for ultravisor.
> 
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> Signed-off-by: Bharata B Rao <bharata@linux.ibm.com>
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> [ Update config help and commit message ]
> Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
  Reviewed-by: Janani Janakiraman <janani@linux.ibm.com>
> ---
>  arch/powerpc/Kconfig | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index f0e5b38d52e8..20c6c213d2be 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -440,6 +440,26 @@ config PPC_TRANSACTIONAL_MEM
>         ---help---
>           Support user-mode Transactional Memory on POWERPC.
> 
> +config PPC_UV
> +	bool "Ultravisor support"
> +	depends on KVM_BOOK3S_HV_POSSIBLE
> +	select HMM_MIRROR
> +	select HMM
> +	select ZONE_DEVICE
> +	select MIGRATE_VMA_HELPER
> +	select DEV_PAGEMAP_OPS
> +	select DEVICE_PRIVATE
> +	select MEMORY_HOTPLUG
> +	select MEMORY_HOTREMOVE
> +	default n
> +	help
> +	  This option paravirtualizes the kernel to run in POWER platforms 
> that
> +	  supports the Protected Execution Facility (PEF). In such platforms,
> +	  the ultravisor firmware runs at a privilege level above the
> +	  hypervisor.
> +
> +	  If unsure, say "N".
> +
>  config LD_HEAD_STUB_CATCH
>  	bool "Reserve 256 bytes to cope with linker stubs in HEAD text" if 
> EXPERT
>  	depends on PPC64


^ permalink raw reply

* Re: [PATCH v2 1/7] kexec: add KEXEC_ELF
From: Christophe Leroy @ 2019-07-10 13:19 UTC (permalink / raw)
  To: Sven Schnelle, kexec; +Cc: deller, linuxppc-dev
In-Reply-To: <20190709194328.16991-2-svens@stackframe.org>

Hi Sven,

On 07/09/2019 07:43 PM, Sven Schnelle wrote:
> Right now powerpc provides an implementation to read elf files
> with the kexec_file() syscall. Make that available as a public
> kexec interface so it can be re-used on other architectures.
> 
> Signed-off-by: Sven Schnelle <svens@stackframe.org>
> ---
>   arch/Kconfig                       |   3 +
>   arch/powerpc/Kconfig               |   1 +
>   arch/powerpc/kernel/kexec_elf_64.c | 551 +----------------------------
>   include/linux/kexec.h              |  24 ++
>   kernel/Makefile                    |   1 +
>   kernel/kexec_elf.c                 | 537 ++++++++++++++++++++++++++++
>   6 files changed, 576 insertions(+), 541 deletions(-)
>   create mode 100644 kernel/kexec_elf.c

Why are you persisting at not using -C when creating your patch ? Do you 
want to hide the changes you did while copying 
arch/powerpc/kernel/kexec_elf_64.c to kernel/kexec_elf.c ?
Or you want to make life harder for the reviewers ?

git format-patch -C shows:

  arch/Kconfig                                       |   3 +
  arch/powerpc/Kconfig                               |   1 +
  arch/powerpc/kernel/kexec_elf_64.c                 | 551 
+--------------------
  include/linux/kexec.h                              |  24 +
  kernel/Makefile                                    |   1 +
  .../kernel/kexec_elf_64.c => kernel/kexec_elf.c    | 199 ++------
  6 files changed, 75 insertions(+), 704 deletions(-)
  copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_elf.c (71%)

I mentionned it a couple of times, I even resent your last patch 
formatted that way to show the advantage. I can ear if you find it 
worthless, but tell what your concern are with that, don't just ignore 
it please.

Cheers
Christophe

> 
> diff --git a/arch/Kconfig b/arch/Kconfig
> index c47b328eada0..30694aca4316 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
>   	select CRASH_CORE
>   	bool
>   
> +config KEXEC_ELF
> +	bool
> +
>   config HAVE_IMA_KEXEC
>   	bool
>   
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 12cee37f15c4..addc2dad78e0 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -510,6 +510,7 @@ config KEXEC_FILE
>   	select KEXEC_CORE
>   	select HAVE_IMA_KEXEC
>   	select BUILD_BIN2C
> +	select KEXEC_ELF
>   	depends on PPC64
>   	depends on CRYPTO=y
>   	depends on CRYPTO_SHA256=y
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..30bd57a93c17 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -1,3 +1,4 @@
> +// SPDX-License-Identifier: GPL-2.0-only
>   /*
>    * Load ELF vmlinux file for the kexec_file_load syscall.
>    *
> @@ -10,15 +11,6 @@
>    * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
>    * Heavily modified for the kernel by
>    * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation (version 2 of the License).
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
>    */
>   
>   #define pr_fmt(fmt)	"kexec_elf: " fmt
> @@ -39,532 +31,6 @@
>   #define Elf_Rel		Elf64_Rel
>   #endif /* Elf_Rel */
>   
> -struct elf_info {
> -	/*
> -	 * Where the ELF binary contents are kept.
> -	 * Memory managed by the user of the struct.
> -	 */
> -	const char *buffer;
> -
> -	const struct elfhdr *ehdr;
> -	const struct elf_phdr *proghdrs;
> -	struct elf_shdr *sechdrs;
> -};
> -
> -static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> -{
> -       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> -}
> -
> -static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le64_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be64_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le16_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be16_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le32_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be32_to_cpu(value);
> -
> -	return value;
> -}
> -
> -/**
> - * elf_is_ehdr_sane - check that it is safe to use the ELF header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> -{
> -	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> -		pr_debug("Bad program header size.\n");
> -		return false;
> -	} else if (ehdr->e_shnum > 0 &&
> -		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> -		pr_debug("Bad section header size.\n");
> -		return false;
> -	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> -		   ehdr->e_version != EV_CURRENT) {
> -		pr_debug("Unknown ELF version.\n");
> -		return false;
> -	}
> -
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		size_t phdr_size;
> -
> -		/*
> -		 * e_phnum is at most 65535 so calculating the size of the
> -		 * program header cannot overflow.
> -		 */
> -		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -		/* Sanity check the program header table location. */
> -		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> -			pr_debug("Program headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> -			pr_debug("Program headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		size_t shdr_size;
> -
> -		/*
> -		 * e_shnum is at most 65536 so calculating
> -		 * the size of the section header cannot overflow.
> -		 */
> -		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> -
> -		/* Sanity check the section header table location. */
> -		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> -			pr_debug("Section headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> -			pr_debug("Section headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> -{
> -	struct elfhdr *buf_ehdr;
> -
> -	if (len < sizeof(*buf_ehdr)) {
> -		pr_debug("Buffer is too small to hold ELF header.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	memset(ehdr, 0, sizeof(*ehdr));
> -	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> -	if (!elf_is_elf_file(ehdr)) {
> -		pr_debug("No ELF header magic.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> -		pr_debug("Not a supported ELF class.\n");
> -		return -ENOEXEC;
> -	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> -		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> -		pr_debug("Not a supported ELF data format.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	buf_ehdr = (struct elfhdr *) buf;
> -	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> -		pr_debug("Bad ELF header size.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> -	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> -	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> -	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> -	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> -	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> -	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> -	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> -	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> -	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> -	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> -	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> -
> -	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_is_phdr_sane - check that it is safe to use the program header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> -{
> -
> -	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> -		pr_debug("ELF segment location wraps around.\n");
> -		return false;
> -	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> -		pr_debug("ELF segment not in file.\n");
> -		return false;
> -	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> -		pr_debug("ELF segment address wraps around.\n");
> -		return false;
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	/* Override the const in proghdrs, we are the ones doing the loading. */
> -	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> -	const char *pbuf;
> -	struct elf_phdr *buf_phdr;
> -
> -	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> -	buf_phdr = (struct elf_phdr *) pbuf;
> -
> -	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> -	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> -	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> -	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> -	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> -	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> -	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> -
> -	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_phdrs - read the program headers from the buffer
> - *
> - * This function assumes that the program header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_phdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t phdr_size, i;
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -
> -	/*
> -	 * e_phnum is at most 65535 so calculating the size of the
> -	 * program header cannot overflow.
> -	 */
> -	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> -	if (!elf_info->proghdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_phdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			elf_info->proghdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_is_shdr_sane - check that it is safe to use the section header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> -{
> -	bool size_ok;
> -
> -	/* SHT_NULL headers have undefined values, so we can't check them. */
> -	if (shdr->sh_type == SHT_NULL)
> -		return true;
> -
> -	/* Now verify sh_entsize */
> -	switch (shdr->sh_type) {
> -	case SHT_SYMTAB:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> -		break;
> -	case SHT_RELA:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> -		break;
> -	case SHT_DYNAMIC:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> -		break;
> -	case SHT_REL:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> -		break;
> -	case SHT_NOTE:
> -	case SHT_PROGBITS:
> -	case SHT_HASH:
> -	case SHT_NOBITS:
> -	default:
> -		/*
> -		 * This is a section whose entsize requirements
> -		 * I don't care about.  If I don't know about
> -		 * the section I can't care about it's entsize
> -		 * requirements.
> -		 */
> -		size_ok = true;
> -		break;
> -	}
> -
> -	if (!size_ok) {
> -		pr_debug("ELF section with wrong entry size.\n");
> -		return false;
> -	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> -		pr_debug("ELF section address wraps around.\n");
> -		return false;
> -	}
> -
> -	if (shdr->sh_type != SHT_NOBITS) {
> -		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> -			pr_debug("ELF section location wraps around.\n");
> -			return false;
> -		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> -			pr_debug("ELF section not in file.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -	const char *sbuf;
> -	struct elf_shdr *buf_shdr;
> -
> -	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> -	buf_shdr = (struct elf_shdr *) sbuf;
> -
> -	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> -	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> -	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> -	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> -	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> -	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> -	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> -	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> -	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> -
> -	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_shdrs - read the section headers from the buffer
> - *
> - * This function assumes that the section header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_shdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t shdr_size, i;
> -
> -	/*
> -	 * e_shnum is at most 65536 so calculating
> -	 * the size of the section header cannot overflow.
> -	 */
> -	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> -
> -	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> -	if (!elf_info->sechdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_shdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->sechdrs);
> -			elf_info->sechdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> - * @buf:	Buffer to read ELF file from.
> - * @len:	Size of @buf.
> - * @ehdr:	Pointer to existing struct which will be populated.
> - * @elf_info:	Pointer to existing struct which will be populated.
> - *
> - * This function allows reading ELF files with different byte order than
> - * the kernel, byte-swapping the fields as needed.
> - *
> - * Return:
> - * On success returns 0, and the caller should call elf_free_info(elf_info) to
> - * free the memory allocated for the section and program headers.
> - */
> -int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info)
> -{
> -	int ret;
> -
> -	ret = elf_read_ehdr(buf, len, ehdr);
> -	if (ret)
> -		return ret;
> -
> -	elf_info->buffer = buf;
> -	elf_info->ehdr = ehdr;
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		ret = elf_read_phdrs(buf, len, elf_info);
> -		if (ret)
> -			return ret;
> -	}
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		ret = elf_read_shdrs(buf, len, elf_info);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_free_info - free memory allocated by elf_read_from_buffer
> - */
> -void elf_free_info(struct elf_info *elf_info)
> -{
> -	kfree(elf_info->proghdrs);
> -	kfree(elf_info->sechdrs);
> -	memset(elf_info, 0, sizeof(*elf_info));
> -}
> -/**
> - * build_elf_exec_info - read ELF executable and check that we can use it
> - */
> -static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
> -			       struct elf_info *elf_info)
> -{
> -	int i;
> -	int ret;
> -
> -	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> -	if (ret)
> -		return ret;
> -
> -	/* Big endian vmlinux has type ET_DYN. */
> -	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> -		pr_err("Not an ELF executable.\n");
> -		goto error;
> -	} else if (!elf_info->proghdrs) {
> -		pr_err("No ELF program header.\n");
> -		goto error;
> -	}
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		/*
> -		 * Kexec does not support loading interpreters.
> -		 * In addition this check keeps us from attempting
> -		 * to kexec ordinay executables.
> -		 */
> -		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> -			pr_err("Requires an ELF interpreter.\n");
> -			goto error;
> -		}
> -	}
> -
> -	return 0;
> -error:
> -	elf_free_info(elf_info);
> -	return -ENOEXEC;
> -}
> -
> -static int elf64_probe(const char *buf, unsigned long len)
> -{
> -	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> -	int ret;
> -
> -	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
> -	if (ret)
> -		return ret;
> -
> -	elf_free_info(&elf_info);
> -
> -	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_exec_load - load ELF executable image
> - * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> - *			section will be loaded in memory.
> - *
> - * Return:
> - * 0 on success, negative value on failure.
> - */
> -static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info,
> -			 unsigned long *lowest_load_addr)
> -{
> -	unsigned long base = 0, lowest_addr = UINT_MAX;
> -	int ret;
> -	size_t i;
> -	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
> -				  .top_down = false };
> -
> -	/* Read in the PT_LOAD segments. */
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		unsigned long load_addr;
> -		size_t size;
> -		const struct elf_phdr *phdr;
> -
> -		phdr = &elf_info->proghdrs[i];
> -		if (phdr->p_type != PT_LOAD)
> -			continue;
> -
> -		size = phdr->p_filesz;
> -		if (size > phdr->p_memsz)
> -			size = phdr->p_memsz;
> -
> -		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
> -		kbuf.bufsz = size;
> -		kbuf.memsz = phdr->p_memsz;
> -		kbuf.buf_align = phdr->p_align;
> -		kbuf.buf_min = phdr->p_paddr + base;
> -		ret = kexec_add_buffer(&kbuf);
> -		if (ret)
> -			goto out;
> -		load_addr = kbuf.mem;
> -
> -		if (load_addr < lowest_addr)
> -			lowest_addr = load_addr;
> -	}
> -
> -	/* Update entry point to reflect new load address. */
> -	ehdr->e_entry += base;
> -
> -	*lowest_load_addr = lowest_addr;
> -	ret = 0;
> - out:
> -	return ret;
> -}
> -
>   static void *elf64_load(struct kimage *image, char *kernel_buf,
>   			unsigned long kernel_len, char *initrd,
>   			unsigned long initrd_len, char *cmdline,
> @@ -577,17 +43,18 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   	void *fdt;
>   	const void *slave_code;
>   	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> +	struct kexec_elf_info elf_info;
>   	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
>   				  .buf_max = ppc64_rma_size };
>   	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
> -				  .buf_max = ppc64_rma_size, .top_down = true };
> +				  .buf_max = ppc64_rma_size, .top_down = true,
> +				  .mem = KEXEC_BUF_MEM_UNKNOWN };
>   
> -	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> +	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
>   	if (ret)
>   		goto out;
>   
> -	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> +	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
>   	if (ret)
>   		goto out;
>   
> @@ -606,6 +73,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   		kbuf.bufsz = kbuf.memsz = initrd_len;
>   		kbuf.buf_align = PAGE_SIZE;
>   		kbuf.top_down = false;
> +		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
>   		ret = kexec_add_buffer(&kbuf);
>   		if (ret)
>   			goto out;
> @@ -638,6 +106,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   	kbuf.bufsz = kbuf.memsz = fdt_size;
>   	kbuf.buf_align = PAGE_SIZE;
>   	kbuf.top_down = true;
> +	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
>   	ret = kexec_add_buffer(&kbuf);
>   	if (ret)
>   		goto out;
> @@ -652,13 +121,13 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   		pr_err("Error setting up the purgatory.\n");
>   
>   out:
> -	elf_free_info(&elf_info);
> +	kexec_free_elf_info(&elf_info);
>   
>   	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
>   	return ret ? ERR_PTR(ret) : fdt;
>   }
>   
>   const struct kexec_file_ops kexec_elf64_ops = {
> -	.probe = elf64_probe,
> +	.probe = kexec_elf_probe,
>   	.load = elf64_load,
>   };
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..da2a6b1d69e7 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,30 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
>   				       void **addr, unsigned long *sz);
>   #endif /* CONFIG_KEXEC_FILE */
>   
> +#ifdef CONFIG_KEXEC_ELF
> +struct kexec_elf_info {
> +	/*
> +	 * Where the ELF binary contents are kept.
> +	 * Memory managed by the user of the struct.
> +	 */
> +	const char *buffer;
> +
> +	const struct elfhdr *ehdr;
> +	const struct elf_phdr *proghdrs;
> +	struct elf_shdr *sechdrs;
> +};
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			       struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +#endif
>   struct kimage {
>   	kimage_entry_t head;
>   	kimage_entry_t *entry;
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 33824f0385b3..7062306de9b7 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
>   obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
>   obj-$(CONFIG_KEXEC) += kexec.o
>   obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
> +obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
>   obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
>   obj-$(CONFIG_COMPAT) += compat.o
>   obj-$(CONFIG_CGROUPS) += cgroup/
> diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
> new file mode 100644
> index 000000000000..6e9f52171ede
> --- /dev/null
> +++ b/kernel/kexec_elf.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#define pr_fmt(fmt)	"kexec_elf: " fmt
> +
> +#include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#define PURGATORY_STACK_SIZE	(16 * 1024)
> +
> +#define elf_addr_to_cpu	elf64_to_cpu
> +
> +#ifndef Elf_Rel
> +#define Elf_Rel		Elf64_Rel
> +#endif /* Elf_Rel */
> +
> +static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> +{
> +	return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> +}
> +
> +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le64_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be64_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le16_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be16_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le32_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be32_to_cpu(value);
> +
> +	return value;
> +}
> +
> +/**
> + * elf_is_ehdr_sane - check that it is safe to use the ELF header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> +{
> +	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> +		pr_debug("Bad program header size.\n");
> +		return false;
> +	} else if (ehdr->e_shnum > 0 &&
> +		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> +		pr_debug("Bad section header size.\n");
> +		return false;
> +	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> +		   ehdr->e_version != EV_CURRENT) {
> +		pr_debug("Unknown ELF version.\n");
> +		return false;
> +	}
> +
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		size_t phdr_size;
> +
> +		/*
> +		 * e_phnum is at most 65535 so calculating the size of the
> +		 * program header cannot overflow.
> +		 */
> +		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +		/* Sanity check the program header table location. */
> +		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> +			pr_debug("Program headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> +			pr_debug("Program headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		size_t shdr_size;
> +
> +		/*
> +		 * e_shnum is at most 65536 so calculating
> +		 * the size of the section header cannot overflow.
> +		 */
> +		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> +
> +		/* Sanity check the section header table location. */
> +		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> +			pr_debug("Section headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> +			pr_debug("Section headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> +{
> +	struct elfhdr *buf_ehdr;
> +
> +	if (len < sizeof(*buf_ehdr)) {
> +		pr_debug("Buffer is too small to hold ELF header.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	memset(ehdr, 0, sizeof(*ehdr));
> +	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> +	if (!elf_is_elf_file(ehdr)) {
> +		pr_debug("No ELF header magic.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> +		pr_debug("Not a supported ELF class.\n");
> +		return -ENOEXEC;
> +	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> +		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> +		pr_debug("Not a supported ELF data format.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	buf_ehdr = (struct elfhdr *) buf;
> +	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> +		pr_debug("Bad ELF header size.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> +	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> +	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> +	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> +	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> +	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> +	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> +	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> +	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> +	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> +	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> +	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> +
> +	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_is_phdr_sane - check that it is safe to use the program header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> +{
> +
> +	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> +		pr_debug("ELF segment location wraps around.\n");
> +		return false;
> +	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> +		pr_debug("ELF segment not in file.\n");
> +		return false;
> +	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> +		pr_debug("ELF segment address wraps around.\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_phdr(const char *buf, size_t len,
> +			 struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	/* Override the const in proghdrs, we are the ones doing the loading. */
> +	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> +	const char *pbuf;
> +	struct elf_phdr *buf_phdr;
> +
> +	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> +	buf_phdr = (struct elf_phdr *) pbuf;
> +
> +	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> +	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> +	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> +	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> +	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> +	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> +	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> +
> +	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_phdrs - read the program headers from the buffer
> + *
> + * This function assumes that the program header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_phdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t phdr_size, i;
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +
> +	/*
> +	 * e_phnum is at most 65535 so calculating the size of the
> +	 * program header cannot overflow.
> +	 */
> +	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> +	if (!elf_info->proghdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_phdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			elf_info->proghdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_is_shdr_sane - check that it is safe to use the section header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> +{
> +	bool size_ok;
> +
> +	/* SHT_NULL headers have undefined values, so we can't check them. */
> +	if (shdr->sh_type == SHT_NULL)
> +		return true;
> +
> +	/* Now verify sh_entsize */
> +	switch (shdr->sh_type) {
> +	case SHT_SYMTAB:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> +		break;
> +	case SHT_RELA:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> +		break;
> +	case SHT_DYNAMIC:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> +		break;
> +	case SHT_REL:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> +		break;
> +	case SHT_NOTE:
> +	case SHT_PROGBITS:
> +	case SHT_HASH:
> +	case SHT_NOBITS:
> +	default:
> +		/*
> +		 * This is a section whose entsize requirements
> +		 * I don't care about.  If I don't know about
> +		 * the section I can't care about it's entsize
> +		 * requirements.
> +		 */
> +		size_ok = true;
> +		break;
> +	}
> +
> +	if (!size_ok) {
> +		pr_debug("ELF section with wrong entry size.\n");
> +		return false;
> +	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> +		pr_debug("ELF section address wraps around.\n");
> +		return false;
> +	}
> +
> +	if (shdr->sh_type != SHT_NOBITS) {
> +		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> +			pr_debug("ELF section location wraps around.\n");
> +			return false;
> +		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> +			pr_debug("ELF section not in file.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_shdr(const char *buf, size_t len,
> +			 struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +	const char *sbuf;
> +	struct elf_shdr *buf_shdr;
> +
> +	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> +	buf_shdr = (struct elf_shdr *) sbuf;
> +
> +	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> +	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> +	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> +	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> +	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> +	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> +	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> +	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> +	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> +
> +	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_shdrs - read the section headers from the buffer
> + *
> + * This function assumes that the section header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_shdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t shdr_size, i;
> +
> +	/*
> +	 * e_shnum is at most 65536 so calculating
> +	 * the size of the section header cannot overflow.
> +	 */
> +	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> +
> +	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> +	if (!elf_info->sechdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_shdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->sechdrs);
> +			elf_info->sechdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> + * @buf:	Buffer to read ELF file from.
> + * @len:	Size of @buf.
> + * @ehdr:	Pointer to existing struct which will be populated.
> + * @elf_info:	Pointer to existing struct which will be populated.
> + *
> + * This function allows reading ELF files with different byte order than
> + * the kernel, byte-swapping the fields as needed.
> + *
> + * Return:
> + * On success returns 0, and the caller should call
> + * kexec_free_elf_info(elf_info) to free the memory allocated for the section
> + * and program headers.
> + */
> +static int elf_read_from_buffer(const char *buf, size_t len,
> +				struct elfhdr *ehdr,
> +				struct kexec_elf_info *elf_info)
> +{
> +	int ret;
> +
> +	ret = elf_read_ehdr(buf, len, ehdr);
> +	if (ret)
> +		return ret;
> +
> +	elf_info->buffer = buf;
> +	elf_info->ehdr = ehdr;
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		ret = elf_read_phdrs(buf, len, elf_info);
> +		if (ret)
> +			return ret;
> +	}
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		ret = elf_read_shdrs(buf, len, elf_info);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> + */
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> +{
> +	kfree(elf_info->proghdrs);
> +	kfree(elf_info->sechdrs);
> +	memset(elf_info, 0, sizeof(*elf_info));
> +}
> +/**
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> + */
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			       struct kexec_elf_info *elf_info)
> +{
> +	int i;
> +	int ret;
> +
> +	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> +	if (ret)
> +		return ret;
> +
> +	/* Big endian vmlinux has type ET_DYN. */
> +	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> +		pr_err("Not an ELF executable.\n");
> +		goto error;
> +	} else if (!elf_info->proghdrs) {
> +		pr_err("No ELF program header.\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		/*
> +		 * Kexec does not support loading interpreters.
> +		 * In addition this check keeps us from attempting
> +		 * to kexec ordinay executables.
> +		 */
> +		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> +			pr_err("Requires an ELF interpreter.\n");
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +error:
> +	kexec_free_elf_info(elf_info);
> +	return -ENOEXEC;
> +}
> +
> +
> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +	int ret;
> +
> +	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> +	if (ret)
> +		return ret;
> +
> +	kexec_free_elf_info(&elf_info);
> +
> +	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * kexec_elf_load - load ELF executable image
> + * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> + *			section will be loaded in memory.
> + *
> + * Return:
> + * 0 on success, negative value on failure.
> + */
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr)
> +{
> +	unsigned long base = 0, lowest_addr = UINT_MAX;
> +	int ret;
> +	size_t i;
> +
> +	/* Read in the PT_LOAD segments. */
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		unsigned long load_addr;
> +		size_t size;
> +		const struct elf_phdr *phdr;
> +
> +		phdr = &elf_info->proghdrs[i];
> +		if (phdr->p_type != PT_LOAD)
> +			continue;
> +
> +		size = phdr->p_filesz;
> +		if (size > phdr->p_memsz)
> +			size = phdr->p_memsz;
> +
> +		kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
> +		kbuf->bufsz = size;
> +		kbuf->memsz = phdr->p_memsz;
> +		kbuf->buf_align = phdr->p_align;
> +		kbuf->buf_min = phdr->p_paddr + base;
> +		ret = kexec_add_buffer(kbuf);
> +		if (ret)
> +			goto out;
> +		load_addr = kbuf->mem;
> +
> +		if (load_addr < lowest_addr)
> +			lowest_addr = load_addr;
> +	}
> +
> +	/* Update entry point to reflect new load address. */
> +	ehdr->e_entry += base;
> +
> +	*lowest_load_addr = lowest_addr;
> +	ret = 0;
> + out:
> +	return ret;
> +}
> +
> +
> 



^ permalink raw reply

* [Bug 204125] New: FTBFS on ppc64 big endian and gcc9 because of -mcall-aixdesc and missing __linux__
From: bugzilla-daemon @ 2019-07-10 13:24 UTC (permalink / raw)
  To: linuxppc-dev

https://bugzilla.kernel.org/show_bug.cgi?id=204125

            Bug ID: 204125
           Summary: FTBFS on ppc64 big endian and gcc9 because of
                    -mcall-aixdesc and missing __linux__
           Product: Platform Specific/Hardware
           Version: 2.5
    Kernel Version: any
          Hardware: PPC-64
                OS: Linux
              Tree: Mainline
            Status: NEW
          Severity: high
          Priority: P1
         Component: PPC-64
          Assignee: platform_ppc-64@kernel-bugs.osdl.org
          Reporter: linux@octaforge.org
        Regression: No

On ppc64 big endian, the kernel builds with `-mcall-aixdesc` which since gcc
9.x removes `__linux__` from the list of macros being defined. This behavior is
supposed to be more correct (as it's in this case nothing but a hack, the flag
should apparently only be used when building for AIX) but sadly it breaks build
since several things within the tree rely on `__linux__` being defined and
`#ifdef` some of their code based on said macro.

Just removing `-mcall-aixdesc` (and using just `-mabi=elfv1`) is however not
enough, as that instead causes countless undefined references to just about
every symbol when linking `vmlinux`. It would seem that `-mcall-aixdesc`
changes the way symbols are declared in a way that is not expected.

Little endian is not affected because that one uses `-mabi=elfv2` exclusively.

For now I worked around it in my distro by explicitly adding `-D__linux__` in
the kbuild where `-mcall-aixdesc` is inserted into flags, and it works, but
that's obviously just a workaround.

I'm not sure what the proper fix would be.

Also, is there any reason not to allow an ELFv2 kernel to be built on big
endian? There are some supposed performance benefits, and ELFv2 itself supports
either endianness. The current kbuild logic pretty much forces ELFv1 for big
endian and ELFv2 for little endian.

-- 
You are receiving this mail because:
You are watching the assignee of the bug.

^ permalink raw reply

* Re: [RFC PATCH v5 6/7] kvmppc: Support reset of secure guest
From: janani @ 2019-07-10 13:30 UTC (permalink / raw)
  To: Bharata B Rao
  Cc: linuxram, cclaudio, kvm-ppc, Linuxppc-dev, linux-mm, jglisse,
	aneesh.kumar, paulus, sukadev, linuxppc-dev
In-Reply-To: <20190709102545.9187-7-bharata@linux.ibm.com>

On 2019-07-09 05:25, Bharata B Rao wrote:
> Add support for reset of secure guest via a new ioctl KVM_PPC_SVM_OFF.
> This ioctl will be issued by QEMU during reset and includes the
> the following steps:
> 
> - Ask UV to terminate the guest via UV_SVM_TERMINATE ucall
> - Unpin the VPA pages so that they can be migrated back to secure
>   side when guest becomes secure again. This is required because
>   pinned pages can't be migrated.
> - Reinitialize guest's partitioned scoped page tables. These are
>   freed when guest become secure (H_SVM_INIT_DONE)
> - Release all HMM pages of the secure guest.
> 
> After these steps, guest is ready to issue UV_ESM call once again
> to switch to secure mode.
> 
> Signed-off-by: Bharata B Rao <bharata@linux.ibm.com>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> 	[Implementation of uv_svm_terminate() and its call from
> 	guest shutdown path]
> Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> 	[Unpinning of VPA pages]
  Reviewed-by: Janani Janakiraman <janani@linux.ibm.com>
> ---
>  Documentation/virtual/kvm/api.txt         | 19 ++++++
>  arch/powerpc/include/asm/kvm_book3s_hmm.h |  7 +++
>  arch/powerpc/include/asm/kvm_ppc.h        |  2 +
>  arch/powerpc/include/asm/ultravisor-api.h |  1 +
>  arch/powerpc/include/asm/ultravisor.h     |  7 +++
>  arch/powerpc/kvm/book3s_hv.c              | 70 +++++++++++++++++++++++
>  arch/powerpc/kvm/book3s_hv_hmm.c          | 60 +++++++++++++++++++
>  arch/powerpc/kvm/powerpc.c                | 12 ++++
>  include/uapi/linux/kvm.h                  |  1 +
>  tools/include/uapi/linux/kvm.h            |  1 +
>  10 files changed, 180 insertions(+)
> 
> diff --git a/Documentation/virtual/kvm/api.txt
> b/Documentation/virtual/kvm/api.txt
> index ba6c42c576dd..c89c24ad86ed 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -4065,6 +4065,25 @@ KVM_ARM_VCPU_FINALIZE call.
>  See KVM_ARM_VCPU_INIT for details of vcpu features that require 
> finalization
>  using this ioctl.
> 
> +4.120 KVM_PPC_SVM_OFF
> +
> +Capability: basic
> +Architectures: powerpc
> +Type: vm ioctl
> +Parameters: none
> +Returns: 0 on successful completion,
> +Errors:
> +  EINVAL:    if ultravisor failed to terminate the secure guest
> +  ENOMEM:    if hypervisor failed to allocate new radix page tables 
> for guest
> +
> +This ioctl is used to turn off the secure mode of the guest or 
> transition
> +the guest from secure mode to normal mode. This is invoked when the 
> guest
> +is reset. This has no effect if called for a normal guest.
> +
> +This ioctl issues an ultravisor call to terminate the secure guest,
> +unpin the VPA pages, reinitialize guest's partition scoped page
> +tables and releases all the HMM pages that is associated with this 
> guest.
> +
>  5. The kvm_run structure
>  ------------------------
> 
> diff --git a/arch/powerpc/include/asm/kvm_book3s_hmm.h
> b/arch/powerpc/include/asm/kvm_book3s_hmm.h
> index 8c7aacabb2e0..cd2b0f927766 100644
> --- a/arch/powerpc/include/asm/kvm_book3s_hmm.h
> +++ b/arch/powerpc/include/asm/kvm_book3s_hmm.h
> @@ -13,6 +13,8 @@ extern unsigned long kvmppc_h_svm_page_out(struct kvm 
> *kvm,
>  					  unsigned long page_shift);
>  extern unsigned long kvmppc_h_svm_init_start(struct kvm *kvm);
>  extern unsigned long kvmppc_h_svm_init_done(struct kvm *kvm);
> +extern void kvmppc_hmm_free_memslot_pfns(struct kvm *kvm,
> +		struct kvm_memslots *slots);
>  #else
>  static inline unsigned long
>  kvmppc_h_svm_page_in(struct kvm *kvm, unsigned long gra,
> @@ -37,5 +39,10 @@ static inline unsigned long
> kvmppc_h_svm_init_done(struct kvm *kvm)
>  {
>  	return H_UNSUPPORTED;
>  }
> +
> +static inline void kvmppc_hmm_free_memslot_pfns(struct kvm *kvm,
> +		struct kvm_memslots *slots)
> +{
> +}
>  #endif /* CONFIG_PPC_UV */
>  #endif /* __POWERPC_KVM_PPC_HMM_H__ */
> diff --git a/arch/powerpc/include/asm/kvm_ppc.h
> b/arch/powerpc/include/asm/kvm_ppc.h
> index bc892380e6cd..d80ece28d65d 100644
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -188,6 +188,7 @@ extern void
> kvm_spapr_tce_release_iommu_group(struct kvm *kvm,
>  extern int kvmppc_switch_mmu_to_hpt(struct kvm *kvm);
>  extern int kvmppc_switch_mmu_to_radix(struct kvm *kvm);
>  extern void kvmppc_setup_partition_table(struct kvm *kvm);
> +extern int kvmppc_reinit_partition_table(struct kvm *kvm);
> 
>  extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
>  				struct kvm_create_spapr_tce_64 *args);
> @@ -332,6 +333,7 @@ struct kvmppc_ops {
>  			       int size);
>  	int (*store_to_eaddr)(struct kvm_vcpu *vcpu, ulong *eaddr, void *ptr,
>  			      int size);
> +	int (*svm_off)(struct kvm *kvm);
>  };
> 
>  extern struct kvmppc_ops *kvmppc_hv_ops;
> diff --git a/arch/powerpc/include/asm/ultravisor-api.h
> b/arch/powerpc/include/asm/ultravisor-api.h
> index 9f5510b55892..c8180427fa01 100644
> --- a/arch/powerpc/include/asm/ultravisor-api.h
> +++ b/arch/powerpc/include/asm/ultravisor-api.h
> @@ -25,5 +25,6 @@
>  #define UV_PAGE_IN			0xF128
>  #define UV_PAGE_OUT			0xF12C
>  #define UV_PAGE_INVAL			0xF138
> +#define UV_SVM_TERMINATE		0xF13C
> 
>  #endif /* _ASM_POWERPC_ULTRAVISOR_API_H */
> diff --git a/arch/powerpc/include/asm/ultravisor.h
> b/arch/powerpc/include/asm/ultravisor.h
> index f4f674794b35..a5e4516c8ddb 100644
> --- a/arch/powerpc/include/asm/ultravisor.h
> +++ b/arch/powerpc/include/asm/ultravisor.h
> @@ -84,6 +84,13 @@ static inline int uv_page_inval(u64 lpid, u64 gpa,
> u64 page_shift)
> 
>  	return ucall(UV_PAGE_INVAL, retbuf, lpid, gpa, page_shift);
>  }
> +
> +static inline int uv_svm_terminate(u64 lpid)
> +{
> +	unsigned long retbuf[UCALL_BUFSIZE];
> +
> +	return ucall(UV_SVM_TERMINATE, retbuf, lpid);
> +}
>  #endif /* !__ASSEMBLY__ */
> 
>  #endif	/* _ASM_POWERPC_ULTRAVISOR_H */
> diff --git a/arch/powerpc/kvm/book3s_hv.c 
> b/arch/powerpc/kvm/book3s_hv.c
> index 7cbb5edaed01..41b0fff199d3 100644
> --- a/arch/powerpc/kvm/book3s_hv.c
> +++ b/arch/powerpc/kvm/book3s_hv.c
> @@ -2437,6 +2437,15 @@ static void unpin_vpa(struct kvm *kvm, struct
> kvmppc_vpa *vpa)
>  					vpa->dirty);
>  }
> 
> +static void unpin_vpa_reset(struct kvm *kvm, struct kvmppc_vpa *vpa)
> +{
> +	unpin_vpa(kvm, vpa);
> +	vpa->gpa = 0;
> +	vpa->pinned_addr = NULL;
> +	vpa->dirty = false;
> +	vpa->update_pending = 0;
> +}
> +
>  static void kvmppc_core_vcpu_free_hv(struct kvm_vcpu *vcpu)
>  {
>  	spin_lock(&vcpu->arch.vpa_update_lock);
> @@ -4577,6 +4586,22 @@ void kvmppc_setup_partition_table(struct kvm 
> *kvm)
>  	kvmhv_set_ptbl_entry(kvm->arch.lpid, dw0, dw1);
>  }
> 
> +/*
> + * Called from KVM_PPC_SVM_OFF ioctl at guest reset time when secure
> + * guest is converted back to normal guest.
> + */
> +int kvmppc_reinit_partition_table(struct kvm *kvm)
> +{
> +	int ret;
> +
> +	ret = kvmppc_init_vm_radix(kvm);
> +	if (ret)
> +		return ret;
> +
> +	kvmppc_setup_partition_table(kvm);
> +	return 0;
> +}
> +
>  /*
>   * Set up HPT (hashed page table) and RMA (real-mode area).
>   * Must be called with kvm->lock held.
> @@ -4956,6 +4981,7 @@ static void kvmppc_core_destroy_vm_hv(struct kvm 
> *kvm)
>  		if (nesting_enabled(kvm))
>  			kvmhv_release_all_nested(kvm);
>  		kvm->arch.process_table = 0;
> +		uv_svm_terminate(kvm->arch.lpid);
>  		kvmhv_set_ptbl_entry(kvm->arch.lpid, 0, 0);
>  	}
>  	kvmppc_free_lpid(kvm->arch.lpid);
> @@ -5397,6 +5423,49 @@ static int kvmhv_store_to_eaddr(struct kvm_vcpu
> *vcpu, ulong *eaddr, void *ptr,
>  	return rc;
>  }
> 
> +/*
> + *  IOCTL handler to turn off secure mode of guest
> + *
> + * - Issue ucall to terminate the guest on the UV side
> + * - Unpin the VPA pages (Enables these pages to be migrated back
> + *   when VM becomes secure again)
> + * - Recreate partition table as the guest is transitioning back to
> + *   normal mode
> + * - Release all HMM pages
> + */
> +static int kvmhv_svm_off(struct kvm *kvm)
> +{
> +	struct kvm_vcpu *vcpu;
> +	int ret = 0;
> +	int i;
> +
> +	if (kvmppc_is_guest_secure(kvm)) {
> +		ret = uv_svm_terminate(kvm->arch.lpid);
> +		if (ret != U_SUCCESS) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +
> +		kvm_for_each_vcpu(i, vcpu, kvm) {
> +			spin_lock(&vcpu->arch.vpa_update_lock);
> +			unpin_vpa_reset(kvm, &vcpu->arch.dtl);
> +			unpin_vpa_reset(kvm, &vcpu->arch.slb_shadow);
> +			unpin_vpa_reset(kvm, &vcpu->arch.vpa);
> +			spin_unlock(&vcpu->arch.vpa_update_lock);
> +		}
> +
> +		ret = kvmppc_reinit_partition_table(kvm);
> +		if (ret)
> +			goto out;
> +		kvm->arch.secure_guest = 0;
> +		for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
> +			kvmppc_hmm_free_memslot_pfns(kvm,
> +			__kvm_memslots(kvm, i));
> +	}
> +out:
> +	return ret;
> +}
> +
>  static struct kvmppc_ops kvm_ops_hv = {
>  	.get_sregs = kvm_arch_vcpu_ioctl_get_sregs_hv,
>  	.set_sregs = kvm_arch_vcpu_ioctl_set_sregs_hv,
> @@ -5439,6 +5508,7 @@ static struct kvmppc_ops kvm_ops_hv = {
>  	.enable_nested = kvmhv_enable_nested,
>  	.load_from_eaddr = kvmhv_load_from_eaddr,
>  	.store_to_eaddr = kvmhv_store_to_eaddr,
> +	.svm_off = kvmhv_svm_off,
>  };
> 
>  static int kvm_init_subcore_bitmap(void)
> diff --git a/arch/powerpc/kvm/book3s_hv_hmm.c 
> b/arch/powerpc/kvm/book3s_hv_hmm.c
> index 9e6c88de456f..165926c069ba 100644
> --- a/arch/powerpc/kvm/book3s_hv_hmm.c
> +++ b/arch/powerpc/kvm/book3s_hv_hmm.c
> @@ -38,6 +38,8 @@
>  #include <linux/kvm_host.h>
>  #include <linux/sched/mm.h>
>  #include <asm/ultravisor.h>
> +#include <asm/kvm_ppc.h>
> +#include <asm/kvm_book3s.h>
> 
>  struct kvmppc_hmm_device {
>  	struct hmm_device *device;
> @@ -98,6 +100,12 @@ unsigned long kvmppc_h_svm_init_done(struct kvm 
> *kvm)
>  		return H_UNSUPPORTED;
> 
>  	kvm->arch.secure_guest |= KVMPPC_SECURE_INIT_DONE;
> +	if (kvm_is_radix(kvm)) {
> +		pr_info("LPID %d went secure, freeing HV side radix pgtables\n",
> +			kvm->arch.lpid);
> +		kvmppc_free_radix(kvm);
> +	}
> +
>  	return H_SUCCESS;
>  }
> 
> @@ -113,6 +121,58 @@ static inline bool kvmppc_is_hmm_pfn(unsigned long 
> pfn)
>  	return !!(pfn & KVMPPC_PFN_HMM);
>  }
> 
> +/*
> + * Drop HMM pages that we maintain for the secure guest
> + *
> + * We mark the pages to be skipped from UV_PAGE_OUT when there is HMM
> + * fault on these pages. Next we get these pages, force HMM fault,
> + * do fault migration to replace the HMM PTEs from QEMU page tables
> + * with normal PTEs from newly allocated pages.
> + */
> +static void kvmppc_hmm_drop_pages(struct kvm_memory_slot *free,
> struct kvm *kvm)
> +{
> +	int i;
> +	struct kvmppc_hmm_page_pvt *pvt;
> +	unsigned long pfn;
> +
> +	for (i = 0; i < free->npages; i++) {
> +		unsigned long *rmap = &free->arch.rmap[i];
> +		struct page *hmm_page;
> +
> +		if (kvmppc_is_hmm_pfn(*rmap)) {
> +			hmm_page = pfn_to_page(*rmap & ~KVMPPC_PFN_HMM);
> +			pvt = (struct kvmppc_hmm_page_pvt *)
> +				hmm_devmem_page_get_drvdata(hmm_page);
> +			pvt->skip_page_out = true;
> +
> +			pfn = gfn_to_pfn(kvm, pvt->gpa >> PAGE_SHIFT);
> +			if (is_error_noslot_pfn(pfn))
> +				continue;
> +			kvm_release_pfn_clean(pfn);
> +		}
> +	}
> +}
> +
> +/*
> + * Called from KVM_PPC_SVM_OFF ioctl when secure guest is reset
> + *
> + * UV has already cleaned up the guest, we release any HMM pages that
> + * we maintain
> + */
> +void kvmppc_hmm_free_memslot_pfns(struct kvm *kvm, struct kvm_memslots 
> *slots)
> +{
> +	struct kvm_memory_slot *memslot;
> +	int srcu_idx;
> +
> +	if (!slots)
> +		return;
> +
> +	srcu_idx = srcu_read_lock(&kvm->srcu);
> +	kvm_for_each_memslot(memslot, slots)
> +		kvmppc_hmm_drop_pages(memslot, kvm);
> +	srcu_read_unlock(&kvm->srcu, srcu_idx);
> +}
> +
>  /*
>   * Get a free HMM PFN from the pool
>   *
> diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
> index 3393b166817a..0c5fae004adf 100644
> --- a/arch/powerpc/kvm/powerpc.c
> +++ b/arch/powerpc/kvm/powerpc.c
> @@ -42,6 +42,8 @@
>  #include <asm/hvcall.h>
>  #include <asm/plpar_wrappers.h>
>  #endif
> +#include <asm/ultravisor.h>
> +#include <asm/kvm_host.h>
> 
>  #include "timing.h"
>  #include "irq.h"
> @@ -2423,6 +2425,16 @@ long kvm_arch_vm_ioctl(struct file *filp,
>  			r = -EFAULT;
>  		break;
>  	}
> +	case KVM_PPC_SVM_OFF: {
> +		struct kvm *kvm = filp->private_data;
> +
> +		r = 0;
> +		if (!kvm->arch.kvm_ops->svm_off)
> +			goto out;
> +
> +		r = kvm->arch.kvm_ops->svm_off(kvm);
> +		break;
> +	}
>  	default: {
>  		struct kvm *kvm = filp->private_data;
>  		r = kvm->arch.kvm_ops->arch_vm_ioctl(filp, ioctl, arg);
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 2fe12b40d503..c1dbdd428025 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1327,6 +1327,7 @@ struct kvm_s390_ucas_mapping {
>  #define KVM_PPC_GET_RMMU_INFO	  _IOW(KVMIO,  0xb0, struct 
> kvm_ppc_rmmu_info)
>  /* Available with KVM_CAP_PPC_GET_CPU_CHAR */
>  #define KVM_PPC_GET_CPU_CHAR	  _IOR(KVMIO,  0xb1, struct 
> kvm_ppc_cpu_char)
> +#define KVM_PPC_SVM_OFF		  _IO(KVMIO,  0xb2)
> 
>  /* ioctl for vm fd */
>  #define KVM_CREATE_DEVICE	  _IOWR(KVMIO,  0xe0, struct 
> kvm_create_device)
> diff --git a/tools/include/uapi/linux/kvm.h 
> b/tools/include/uapi/linux/kvm.h
> index 6d4ea4b6c922..03c89f4a01b2 100644
> --- a/tools/include/uapi/linux/kvm.h
> +++ b/tools/include/uapi/linux/kvm.h
> @@ -1319,6 +1319,7 @@ struct kvm_s390_ucas_mapping {
>  #define KVM_PPC_GET_RMMU_INFO	  _IOW(KVMIO,  0xb0, struct 
> kvm_ppc_rmmu_info)
>  /* Available with KVM_CAP_PPC_GET_CPU_CHAR */
>  #define KVM_PPC_GET_CPU_CHAR	  _IOR(KVMIO,  0xb1, struct 
> kvm_ppc_cpu_char)
> +#define KVM_PPC_SVM_OFF		  _IO(KVMIO,  0xb2)
> 
>  /* ioctl for vm fd */
>  #define KVM_CREATE_DEVICE	  _IOWR(KVMIO,  0xe0, struct 
> kvm_create_device)

^ permalink raw reply

* Re: [PATCH v5 1/7] kvmppc: HMM backend driver to manage pages of secure guest
From: Jason Gunthorpe @ 2019-07-10 13:47 UTC (permalink / raw)
  To: janani
  Cc: Linuxppc-dev, linuxram, cclaudio, kvm-ppc, Bharata B Rao,
	linux-mm, jglisse, aneesh.kumar, paulus, sukadev, linuxppc-dev
In-Reply-To: <29e536f225036d2a93e653c56a961fcb@linux.vnet.ibm.com>

On Tue, Jul 09, 2019 at 01:55:28PM -0500, janani wrote:

> > +int kvmppc_hmm_init(void)
> > +{
> > +	int ret = 0;
> > +	unsigned long size;
> > +
> > +	size = kvmppc_get_secmem_size();
> > +	if (!size) {
> > +		ret = -ENODEV;
> > +		goto out;
> > +	}
> > +
> > +	kvmppc_hmm.device = hmm_device_new(NULL);
> > +	if (IS_ERR(kvmppc_hmm.device)) {
> > +		ret = PTR_ERR(kvmppc_hmm.device);
> > +		goto out;
> > +	}
> > +
> > +	kvmppc_hmm.devmem = hmm_devmem_add(&kvmppc_hmm_devmem_ops,
> > +					   &kvmppc_hmm.device->device, size);
> > +	if (IS_ERR(kvmppc_hmm.devmem)) {
> > +		ret = PTR_ERR(kvmppc_hmm.devmem);
> > +		goto out_device;
> > +	}

This 'hmm_device' API family was recently deleted from hmm:

commit 07ec38917e68f0114b9c8aeeb1c584b5e73e4dd6
Author: Christoph Hellwig <hch@lst.de>
Date:   Wed Jun 26 14:27:01 2019 +0200

    mm: remove the struct hmm_device infrastructure
    
    This code is a trivial wrapper around device model helpers, which
    should have been integrated into the driver device model usage from
    the start.  Assuming it actually had users, which it never had since
    the code was added more than 1 1/2 years ago.

This patch should use the driver core directly instead.

Regards,
Jason

^ permalink raw reply

* Re: [PATCH v5 7/7] KVM: PPC: Ultravisor: Add PPC_UV config option
From: Jason Gunthorpe @ 2019-07-10 14:15 UTC (permalink / raw)
  To: janani
  Cc: Linuxppc-dev, linuxram, cclaudio, kvm-ppc, Bharata B Rao,
	linux-mm, jglisse, aneesh.kumar, paulus, sukadev, linuxppc-dev,
	Anshuman Khandual
In-Reply-To: <6759c8a79b2962d07ed99f2b1cd05637@linux.vnet.ibm.com>

On Wed, Jul 10, 2019 at 08:24:56AM -0500, janani wrote:
> On 2019-07-09 05:25, Bharata B Rao wrote:
> > From: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> > 
> > CONFIG_PPC_UV adds support for ultravisor.
> > 
> > Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> > Signed-off-by: Bharata B Rao <bharata@linux.ibm.com>
> > Signed-off-by: Ram Pai <linuxram@us.ibm.com>
> > [ Update config help and commit message ]
> > Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
>  Reviewed-by: Janani Janakiraman <janani@linux.ibm.com>
> >  arch/powerpc/Kconfig | 20 ++++++++++++++++++++
> >  1 file changed, 20 insertions(+)
> > 
> > diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> > index f0e5b38d52e8..20c6c213d2be 100644
> > +++ b/arch/powerpc/Kconfig
> > @@ -440,6 +440,26 @@ config PPC_TRANSACTIONAL_MEM
> >           Support user-mode Transactional Memory on POWERPC.
> > 
> > +config PPC_UV
> > +	bool "Ultravisor support"
> > +	depends on KVM_BOOK3S_HV_POSSIBLE
> > +	select HMM_MIRROR
> > +	select HMM
> > +	select ZONE_DEVICE

These configs have also been changed lately, I didn't see any calls to
hmm_mirror in this patchset, so most likely the two HMM selects should
be dropped and all you'll need is ZONE_DEVICE..

Jason

^ permalink raw reply

* Re: [PATCH] powerpc: mm: Limit rma_size to 1TB when running without HV mode
From: David Gibson @ 2019-07-10 14:21 UTC (permalink / raw)
  To: Suraj Jitindar Singh; +Cc: linuxppc-dev, kvm-ppc
In-Reply-To: <20190710052018.14628-1-sjitindarsingh@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3171 bytes --]

On Wed, Jul 10, 2019 at 03:20:18PM +1000, Suraj Jitindar Singh wrote:
> The virtual real mode addressing (VRMA) mechanism is used when a
> partition is using HPT (Hash Page Table) translation and performs
> real mode accesses (MSR[IR|DR] = 0) in non-hypervisor mode. In this
> mode effective address bits 0:23 are treated as zero (i.e. the access
> is aliased to 0) and the access is performed using an implicit 1TB SLB
> entry.
> 
> The size of the RMA (Real Memory Area) is communicated to the guest as
> the size of the first memory region in the device tree. And because of
> the mechanism described above can be expected to not exceed 1TB. In the
> event that the host erroneously represents the RMA as being larger than
> 1TB, guest accesses in real mode to memory addresses above 1TB will be
> aliased down to below 1TB. This means that a memory access performed in
> real mode may differ to one performed in virtual mode for the same memory
> address, which would likely have unintended consequences.
> 
> To avoid this outcome have the guest explicitly limit the size of the
> RMA to the current maximum, which is 1TB. This means that even if the
> first memory block is larger than 1TB, only the first 1TB should be
> accessed in real mode.
> 
> Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

Although I'd really like to also see some comments added in
allocate_paca_ptrs() explaining the constraints there.

Oh, also, basing this on the non-compat PVR is bogus, but it's still
better than what we had.

> ---
>  arch/powerpc/mm/book3s64/hash_utils.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c
> index 28ced26f2a00..4d0e2cce9cd5 100644
> --- a/arch/powerpc/mm/book3s64/hash_utils.c
> +++ b/arch/powerpc/mm/book3s64/hash_utils.c
> @@ -1901,11 +1901,19 @@ void hash__setup_initial_memory_limit(phys_addr_t first_memblock_base,
>  	 *
>  	 * For guests on platforms before POWER9, we clamp the it limit to 1G
>  	 * to avoid some funky things such as RTAS bugs etc...
> +	 * On POWER9 we limit to 1TB in case the host erroneously told us that
> +	 * the RMA was >1TB. Effective address bits 0:23 are treated as zero
> +	 * (meaning the access is aliased to zero i.e. addr = addr % 1TB)
> +	 * for virtual real mode addressing and so it doesn't make sense to
> +	 * have an area larger than 1TB as it can't be addressed.
>  	 */
>  	if (!early_cpu_has_feature(CPU_FTR_HVMODE)) {
>  		ppc64_rma_size = first_memblock_size;
>  		if (!early_cpu_has_feature(CPU_FTR_ARCH_300))
>  			ppc64_rma_size = min_t(u64, ppc64_rma_size, 0x40000000);
> +		else
> +			ppc64_rma_size = min_t(u64, ppc64_rma_size,
> +					       1UL << SID_SHIFT_1T);
>  
>  		/* Finally limit subsequent allocations */
>  		memblock_set_current_limit(ppc64_rma_size);

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH v2 1/7] kexec: add KEXEC_ELF
From: Sven Schnelle @ 2019-07-10 14:24 UTC (permalink / raw)
  To: Christophe Leroy; +Cc: linuxppc-dev, deller, kexec
In-Reply-To: <64306f6c-0a0d-a6ea-537c-5e72deaba1c0@c-s.fr>

Hi Christophe,

On Wed, Jul 10, 2019 at 01:19:13PM +0000, Christophe Leroy wrote:
> Hi Sven,
> 
> On 07/09/2019 07:43 PM, Sven Schnelle wrote:
> > Right now powerpc provides an implementation to read elf files
> > with the kexec_file() syscall. Make that available as a public
> > kexec interface so it can be re-used on other architectures.
> > 
> > Signed-off-by: Sven Schnelle <svens@stackframe.org>
> > ---
> >   arch/Kconfig                       |   3 +
> >   arch/powerpc/Kconfig               |   1 +
> >   arch/powerpc/kernel/kexec_elf_64.c | 551 +----------------------------
> >   include/linux/kexec.h              |  24 ++
> >   kernel/Makefile                    |   1 +
> >   kernel/kexec_elf.c                 | 537 ++++++++++++++++++++++++++++
> >   6 files changed, 576 insertions(+), 541 deletions(-)
> >   create mode 100644 kernel/kexec_elf.c
> 
> Why are you persisting at not using -C when creating your patch ? Do you
> want to hide the changes you did while copying
> arch/powerpc/kernel/kexec_elf_64.c to kernel/kexec_elf.c ?
> Or you want to make life harder for the reviewers ?

Sorry, never used -C before. I used:

git send-email --annotate -v2 -7 --to kexec@lists.infradead.org --cc deller@gmx.de,linuxppc-dev@lists.ozlabs.org -v --format-patch -C -M

However, it looks like it only works when started this way:

git send-email --format-patch -M -C --annotate -v2 -7 --to kexec@lists.infradead.org --cc deller@gmx.de,linuxppc-dev@lists.ozlabs.org -v

I'll resend v2.

Best Regards,
Sven

^ permalink raw reply

* [PATCH v3 0/7] kexec: add generic support for elf kernel images
From: Sven Schnelle @ 2019-07-10 14:29 UTC (permalink / raw)
  To: kexec; +Cc: Sven Schnelle, deller, linuxppc-dev

Hi List,

this is the same changeset as v2, but (hopefully) with git format-patch -C.

Changes to v2:
 - use git format-patch -C

Changes to v1:
 - split up patch into smaller pieces
 - rebase onto powerpc/next
 - remove unused variable in kexec_elf_load()

Changes to RFC version:
 - remove unused Elf_Rel macro
 - remove section header parsing
 - remove PURGATORY_STACK_SIZE
 - change order of elf_*_to_cpu() functions
 - remove elf_addr_to_cpu macro

Sven Schnelle (7):
  kexec: add KEXEC_ELF
  kexec_elf: change order of elf_*_to_cpu() functions
  kexec_elf: remove parsing of section headers
  kexec_elf: remove PURGATORY_STACK_SIZE
  kexec_elf: remove elf_addr_to_cpu macro
  kexec_elf: remove Elf_Rel macro
  kexec_elf: remove unused variable in kexec_elf_load()

 arch/Kconfig                       |   3 +
 arch/powerpc/Kconfig               |   1 +
 arch/powerpc/kernel/kexec_elf_64.c | 551 +----------------------------
 include/linux/kexec.h              |  23 ++
 kernel/Makefile                    |   1 +
 kernel/kexec_elf.c                 | 389 ++++++++++++++++++++
 6 files changed, 427 insertions(+), 541 deletions(-)
 create mode 100644 kernel/kexec_elf.c

-- 
2.20.1


^ permalink raw reply

* [PATCH v3 6/7] kexec_elf: remove Elf_Rel macro
From: Sven Schnelle @ 2019-07-10 14:29 UTC (permalink / raw)
  To: kexec; +Cc: Sven Schnelle, deller, linuxppc-dev
In-Reply-To: <20190710142944.2774-1-svens@stackframe.org>

It wasn't used anywhere, so lets drop it.

Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
 kernel/kexec_elf.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
index 99e6d63b5dfc..b7e47ddd7cad 100644
--- a/kernel/kexec_elf.c
+++ b/kernel/kexec_elf.c
@@ -8,10 +8,6 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 
-#ifndef Elf_Rel
-#define Elf_Rel		Elf64_Rel
-#endif /* Elf_Rel */
-
 static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
 {
 	return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
-- 
2.20.1


^ permalink raw reply related

* [PATCH v3 1/7] kexec: add KEXEC_ELF
From: Sven Schnelle @ 2019-07-10 14:29 UTC (permalink / raw)
  To: kexec; +Cc: Sven Schnelle, deller, linuxppc-dev
In-Reply-To: <20190710142944.2774-1-svens@stackframe.org>

Right now powerpc provides an implementation to read elf files
with the kexec_file() syscall. Make that available as a public
kexec interface so it can be re-used on other architectures.

Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
 arch/Kconfig                                  |   3 +
 arch/powerpc/Kconfig                          |   1 +
 arch/powerpc/kernel/kexec_elf_64.c            | 551 +-----------------
 include/linux/kexec.h                         |  24 +
 kernel/Makefile                               |   1 +
 .../kexec_elf_64.c => kernel/kexec_elf.c      | 199 ++-----
 6 files changed, 75 insertions(+), 704 deletions(-)
 copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_elf.c (71%)

diff --git a/arch/Kconfig b/arch/Kconfig
index c47b328eada0..30694aca4316 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -18,6 +18,9 @@ config KEXEC_CORE
 	select CRASH_CORE
 	bool
 
+config KEXEC_ELF
+	bool
+
 config HAVE_IMA_KEXEC
 	bool
 
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 12cee37f15c4..addc2dad78e0 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -510,6 +510,7 @@ config KEXEC_FILE
 	select KEXEC_CORE
 	select HAVE_IMA_KEXEC
 	select BUILD_BIN2C
+	select KEXEC_ELF
 	depends on PPC64
 	depends on CRYPTO=y
 	depends on CRYPTO_SHA256=y
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
index ba4f18a43ee8..30bd57a93c17 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/arch/powerpc/kernel/kexec_elf_64.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Load ELF vmlinux file for the kexec_file_load syscall.
  *
@@ -10,15 +11,6 @@
  * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
  * Heavily modified for the kernel by
  * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation (version 2 of the License).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #define pr_fmt(fmt)	"kexec_elf: " fmt
@@ -39,532 +31,6 @@
 #define Elf_Rel		Elf64_Rel
 #endif /* Elf_Rel */
 
-struct elf_info {
-	/*
-	 * Where the ELF binary contents are kept.
-	 * Memory managed by the user of the struct.
-	 */
-	const char *buffer;
-
-	const struct elfhdr *ehdr;
-	const struct elf_phdr *proghdrs;
-	struct elf_shdr *sechdrs;
-};
-
-static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
-{
-       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
-}
-
-static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le64_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be64_to_cpu(value);
-
-	return value;
-}
-
-static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le16_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be16_to_cpu(value);
-
-	return value;
-}
-
-static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le32_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be32_to_cpu(value);
-
-	return value;
-}
-
-/**
- * elf_is_ehdr_sane - check that it is safe to use the ELF header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
-{
-	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
-		pr_debug("Bad program header size.\n");
-		return false;
-	} else if (ehdr->e_shnum > 0 &&
-		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
-		pr_debug("Bad section header size.\n");
-		return false;
-	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
-		   ehdr->e_version != EV_CURRENT) {
-		pr_debug("Unknown ELF version.\n");
-		return false;
-	}
-
-	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
-		size_t phdr_size;
-
-		/*
-		 * e_phnum is at most 65535 so calculating the size of the
-		 * program header cannot overflow.
-		 */
-		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
-		/* Sanity check the program header table location. */
-		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
-			pr_debug("Program headers at invalid location.\n");
-			return false;
-		} else if (ehdr->e_phoff + phdr_size > buf_len) {
-			pr_debug("Program headers truncated.\n");
-			return false;
-		}
-	}
-
-	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
-		size_t shdr_size;
-
-		/*
-		 * e_shnum is at most 65536 so calculating
-		 * the size of the section header cannot overflow.
-		 */
-		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
-
-		/* Sanity check the section header table location. */
-		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
-			pr_debug("Section headers at invalid location.\n");
-			return false;
-		} else if (ehdr->e_shoff + shdr_size > buf_len) {
-			pr_debug("Section headers truncated.\n");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
-{
-	struct elfhdr *buf_ehdr;
-
-	if (len < sizeof(*buf_ehdr)) {
-		pr_debug("Buffer is too small to hold ELF header.\n");
-		return -ENOEXEC;
-	}
-
-	memset(ehdr, 0, sizeof(*ehdr));
-	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
-	if (!elf_is_elf_file(ehdr)) {
-		pr_debug("No ELF header magic.\n");
-		return -ENOEXEC;
-	}
-
-	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
-		pr_debug("Not a supported ELF class.\n");
-		return -ENOEXEC;
-	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
-		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
-		pr_debug("Not a supported ELF data format.\n");
-		return -ENOEXEC;
-	}
-
-	buf_ehdr = (struct elfhdr *) buf;
-	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
-		pr_debug("Bad ELF header size.\n");
-		return -ENOEXEC;
-	}
-
-	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
-	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
-	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
-	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
-	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
-	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
-	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
-	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
-	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
-	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
-	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
-	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
-
-	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_is_phdr_sane - check that it is safe to use the program header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
-{
-
-	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
-		pr_debug("ELF segment location wraps around.\n");
-		return false;
-	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
-		pr_debug("ELF segment not in file.\n");
-		return false;
-	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
-		pr_debug("ELF segment address wraps around.\n");
-		return false;
-	}
-
-	return true;
-}
-
-static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
-			 int idx)
-{
-	/* Override the const in proghdrs, we are the ones doing the loading. */
-	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
-	const char *pbuf;
-	struct elf_phdr *buf_phdr;
-
-	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
-	buf_phdr = (struct elf_phdr *) pbuf;
-
-	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
-	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
-	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
-	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
-	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
-
-	/*
-	 * The following fields have a type equivalent to Elf_Addr
-	 * both in 32 bit and 64 bit ELF.
-	 */
-	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
-	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
-	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
-
-	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_phdrs - read the program headers from the buffer
- *
- * This function assumes that the program header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_phdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
-{
-	size_t phdr_size, i;
-	const struct elfhdr *ehdr = elf_info->ehdr;
-
-	/*
-	 * e_phnum is at most 65535 so calculating the size of the
-	 * program header cannot overflow.
-	 */
-	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
-	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
-	if (!elf_info->proghdrs)
-		return -ENOMEM;
-
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		int ret;
-
-		ret = elf_read_phdr(buf, len, elf_info, i);
-		if (ret) {
-			kfree(elf_info->proghdrs);
-			elf_info->proghdrs = NULL;
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_is_shdr_sane - check that it is safe to use the section header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
-{
-	bool size_ok;
-
-	/* SHT_NULL headers have undefined values, so we can't check them. */
-	if (shdr->sh_type == SHT_NULL)
-		return true;
-
-	/* Now verify sh_entsize */
-	switch (shdr->sh_type) {
-	case SHT_SYMTAB:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
-		break;
-	case SHT_RELA:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
-		break;
-	case SHT_DYNAMIC:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
-		break;
-	case SHT_REL:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
-		break;
-	case SHT_NOTE:
-	case SHT_PROGBITS:
-	case SHT_HASH:
-	case SHT_NOBITS:
-	default:
-		/*
-		 * This is a section whose entsize requirements
-		 * I don't care about.  If I don't know about
-		 * the section I can't care about it's entsize
-		 * requirements.
-		 */
-		size_ok = true;
-		break;
-	}
-
-	if (!size_ok) {
-		pr_debug("ELF section with wrong entry size.\n");
-		return false;
-	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
-		pr_debug("ELF section address wraps around.\n");
-		return false;
-	}
-
-	if (shdr->sh_type != SHT_NOBITS) {
-		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
-			pr_debug("ELF section location wraps around.\n");
-			return false;
-		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
-			pr_debug("ELF section not in file.\n");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
-			 int idx)
-{
-	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
-	const struct elfhdr *ehdr = elf_info->ehdr;
-	const char *sbuf;
-	struct elf_shdr *buf_shdr;
-
-	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
-	buf_shdr = (struct elf_shdr *) sbuf;
-
-	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
-	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
-	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
-	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
-	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
-	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
-
-	/*
-	 * The following fields have a type equivalent to Elf_Addr
-	 * both in 32 bit and 64 bit ELF.
-	 */
-	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
-	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
-	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
-	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
-
-	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_shdrs - read the section headers from the buffer
- *
- * This function assumes that the section header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_shdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
-{
-	size_t shdr_size, i;
-
-	/*
-	 * e_shnum is at most 65536 so calculating
-	 * the size of the section header cannot overflow.
-	 */
-	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
-
-	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
-	if (!elf_info->sechdrs)
-		return -ENOMEM;
-
-	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
-		int ret;
-
-		ret = elf_read_shdr(buf, len, elf_info, i);
-		if (ret) {
-			kfree(elf_info->sechdrs);
-			elf_info->sechdrs = NULL;
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
- * @buf:	Buffer to read ELF file from.
- * @len:	Size of @buf.
- * @ehdr:	Pointer to existing struct which will be populated.
- * @elf_info:	Pointer to existing struct which will be populated.
- *
- * This function allows reading ELF files with different byte order than
- * the kernel, byte-swapping the fields as needed.
- *
- * Return:
- * On success returns 0, and the caller should call elf_free_info(elf_info) to
- * free the memory allocated for the section and program headers.
- */
-int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
-			 struct elf_info *elf_info)
-{
-	int ret;
-
-	ret = elf_read_ehdr(buf, len, ehdr);
-	if (ret)
-		return ret;
-
-	elf_info->buffer = buf;
-	elf_info->ehdr = ehdr;
-	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
-		ret = elf_read_phdrs(buf, len, elf_info);
-		if (ret)
-			return ret;
-	}
-	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
-		ret = elf_read_shdrs(buf, len, elf_info);
-		if (ret) {
-			kfree(elf_info->proghdrs);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_free_info - free memory allocated by elf_read_from_buffer
- */
-void elf_free_info(struct elf_info *elf_info)
-{
-	kfree(elf_info->proghdrs);
-	kfree(elf_info->sechdrs);
-	memset(elf_info, 0, sizeof(*elf_info));
-}
-/**
- * build_elf_exec_info - read ELF executable and check that we can use it
- */
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
-			       struct elf_info *elf_info)
-{
-	int i;
-	int ret;
-
-	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
-	if (ret)
-		return ret;
-
-	/* Big endian vmlinux has type ET_DYN. */
-	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
-		pr_err("Not an ELF executable.\n");
-		goto error;
-	} else if (!elf_info->proghdrs) {
-		pr_err("No ELF program header.\n");
-		goto error;
-	}
-
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		/*
-		 * Kexec does not support loading interpreters.
-		 * In addition this check keeps us from attempting
-		 * to kexec ordinay executables.
-		 */
-		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
-			pr_err("Requires an ELF interpreter.\n");
-			goto error;
-		}
-	}
-
-	return 0;
-error:
-	elf_free_info(elf_info);
-	return -ENOEXEC;
-}
-
-static int elf64_probe(const char *buf, unsigned long len)
-{
-	struct elfhdr ehdr;
-	struct elf_info elf_info;
-	int ret;
-
-	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
-	if (ret)
-		return ret;
-
-	elf_free_info(&elf_info);
-
-	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_exec_load - load ELF executable image
- * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
- *			section will be loaded in memory.
- *
- * Return:
- * 0 on success, negative value on failure.
- */
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
-			 struct elf_info *elf_info,
-			 unsigned long *lowest_load_addr)
-{
-	unsigned long base = 0, lowest_addr = UINT_MAX;
-	int ret;
-	size_t i;
-	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
-				  .top_down = false };
-
-	/* Read in the PT_LOAD segments. */
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		unsigned long load_addr;
-		size_t size;
-		const struct elf_phdr *phdr;
-
-		phdr = &elf_info->proghdrs[i];
-		if (phdr->p_type != PT_LOAD)
-			continue;
-
-		size = phdr->p_filesz;
-		if (size > phdr->p_memsz)
-			size = phdr->p_memsz;
-
-		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
-		kbuf.bufsz = size;
-		kbuf.memsz = phdr->p_memsz;
-		kbuf.buf_align = phdr->p_align;
-		kbuf.buf_min = phdr->p_paddr + base;
-		ret = kexec_add_buffer(&kbuf);
-		if (ret)
-			goto out;
-		load_addr = kbuf.mem;
-
-		if (load_addr < lowest_addr)
-			lowest_addr = load_addr;
-	}
-
-	/* Update entry point to reflect new load address. */
-	ehdr->e_entry += base;
-
-	*lowest_load_addr = lowest_addr;
-	ret = 0;
- out:
-	return ret;
-}
-
 static void *elf64_load(struct kimage *image, char *kernel_buf,
 			unsigned long kernel_len, char *initrd,
 			unsigned long initrd_len, char *cmdline,
@@ -577,17 +43,18 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 	void *fdt;
 	const void *slave_code;
 	struct elfhdr ehdr;
-	struct elf_info elf_info;
+	struct kexec_elf_info elf_info;
 	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
 				  .buf_max = ppc64_rma_size };
 	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
-				  .buf_max = ppc64_rma_size, .top_down = true };
+				  .buf_max = ppc64_rma_size, .top_down = true,
+				  .mem = KEXEC_BUF_MEM_UNKNOWN };
 
-	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
 	if (ret)
 		goto out;
 
-	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
+	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
 	if (ret)
 		goto out;
 
@@ -606,6 +73,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 		kbuf.bufsz = kbuf.memsz = initrd_len;
 		kbuf.buf_align = PAGE_SIZE;
 		kbuf.top_down = false;
+		kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
 		ret = kexec_add_buffer(&kbuf);
 		if (ret)
 			goto out;
@@ -638,6 +106,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 	kbuf.bufsz = kbuf.memsz = fdt_size;
 	kbuf.buf_align = PAGE_SIZE;
 	kbuf.top_down = true;
+	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
 	ret = kexec_add_buffer(&kbuf);
 	if (ret)
 		goto out;
@@ -652,13 +121,13 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 		pr_err("Error setting up the purgatory.\n");
 
 out:
-	elf_free_info(&elf_info);
+	kexec_free_elf_info(&elf_info);
 
 	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
 	return ret ? ERR_PTR(ret) : fdt;
 }
 
 const struct kexec_file_ops kexec_elf64_ops = {
-	.probe = elf64_probe,
+	.probe = kexec_elf_probe,
 	.load = elf64_load,
 };
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index b9b1bc5f9669..da2a6b1d69e7 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -216,6 +216,30 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
 				       void **addr, unsigned long *sz);
 #endif /* CONFIG_KEXEC_FILE */
 
+#ifdef CONFIG_KEXEC_ELF
+struct kexec_elf_info {
+	/*
+	 * Where the ELF binary contents are kept.
+	 * Memory managed by the user of the struct.
+	 */
+	const char *buffer;
+
+	const struct elfhdr *ehdr;
+	const struct elf_phdr *proghdrs;
+	struct elf_shdr *sechdrs;
+};
+
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+			       struct kexec_elf_info *elf_info);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info,
+			 struct kexec_buf *kbuf,
+			 unsigned long *lowest_load_addr);
+
+void kexec_free_elf_info(struct kexec_elf_info *elf_info);
+int kexec_elf_probe(const char *buf, unsigned long len);
+#endif
 struct kimage {
 	kimage_entry_t head;
 	kimage_entry_t *entry;
diff --git a/kernel/Makefile b/kernel/Makefile
index 33824f0385b3..7062306de9b7 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
 obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
+obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
 obj-$(CONFIG_COMPAT) += compat.o
 obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/kernel/kexec_elf.c
similarity index 71%
copy from arch/powerpc/kernel/kexec_elf_64.c
copy to kernel/kexec_elf.c
index ba4f18a43ee8..6e9f52171ede 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/kernel/kexec_elf.c
@@ -1,33 +1,10 @@
-/*
- * Load ELF vmlinux file for the kexec_file_load syscall.
- *
- * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
- * Copyright (C) 2004  IBM Corp.
- * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
- * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
- * Copyright (C) 2016  IBM Corporation
- *
- * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
- * Heavily modified for the kernel by
- * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation (version 2 of the License).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
 
 #define pr_fmt(fmt)	"kexec_elf: " fmt
 
 #include <linux/elf.h>
 #include <linux/kexec.h>
-#include <linux/libfdt.h>
 #include <linux/module.h>
-#include <linux/of_fdt.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 
@@ -39,21 +16,9 @@
 #define Elf_Rel		Elf64_Rel
 #endif /* Elf_Rel */
 
-struct elf_info {
-	/*
-	 * Where the ELF binary contents are kept.
-	 * Memory managed by the user of the struct.
-	 */
-	const char *buffer;
-
-	const struct elfhdr *ehdr;
-	const struct elf_phdr *proghdrs;
-	struct elf_shdr *sechdrs;
-};
-
 static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
 {
-       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+	return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
 }
 
 static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
@@ -214,7 +179,8 @@ static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
 	return true;
 }
 
-static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
+static int elf_read_phdr(const char *buf, size_t len,
+			 struct kexec_elf_info *elf_info,
 			 int idx)
 {
 	/* Override the const in proghdrs, we are the ones doing the loading. */
@@ -249,7 +215,7 @@ static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
  * Use elf_is_ehdr_sane() if it wasn't.
  */
 static int elf_read_phdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
+			  struct kexec_elf_info *elf_info)
 {
 	size_t phdr_size, i;
 	const struct elfhdr *ehdr = elf_info->ehdr;
@@ -340,7 +306,8 @@ static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
 	return true;
 }
 
-static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
+static int elf_read_shdr(const char *buf, size_t len,
+			 struct kexec_elf_info *elf_info,
 			 int idx)
 {
 	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
@@ -377,7 +344,7 @@ static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
  * Use elf_is_ehdr_sane() if it wasn't.
  */
 static int elf_read_shdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
+			  struct kexec_elf_info *elf_info)
 {
 	size_t shdr_size, i;
 
@@ -416,11 +383,13 @@ static int elf_read_shdrs(const char *buf, size_t len,
  * the kernel, byte-swapping the fields as needed.
  *
  * Return:
- * On success returns 0, and the caller should call elf_free_info(elf_info) to
- * free the memory allocated for the section and program headers.
+ * On success returns 0, and the caller should call
+ * kexec_free_elf_info(elf_info) to free the memory allocated for the section
+ * and program headers.
  */
-int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
-			 struct elf_info *elf_info)
+static int elf_read_from_buffer(const char *buf, size_t len,
+				struct elfhdr *ehdr,
+				struct kexec_elf_info *elf_info)
 {
 	int ret;
 
@@ -447,19 +416,19 @@ int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
 }
 
 /**
- * elf_free_info - free memory allocated by elf_read_from_buffer
+ * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
  */
-void elf_free_info(struct elf_info *elf_info)
+void kexec_free_elf_info(struct kexec_elf_info *elf_info)
 {
 	kfree(elf_info->proghdrs);
 	kfree(elf_info->sechdrs);
 	memset(elf_info, 0, sizeof(*elf_info));
 }
 /**
- * build_elf_exec_info - read ELF executable and check that we can use it
+ * kexec_build_elf_info - read ELF executable and check that we can use it
  */
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
-			       struct elf_info *elf_info)
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+			       struct kexec_elf_info *elf_info)
 {
 	int i;
 	int ret;
@@ -491,42 +460,42 @@ static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
 
 	return 0;
 error:
-	elf_free_info(elf_info);
+	kexec_free_elf_info(elf_info);
 	return -ENOEXEC;
 }
 
-static int elf64_probe(const char *buf, unsigned long len)
+
+int kexec_elf_probe(const char *buf, unsigned long len)
 {
 	struct elfhdr ehdr;
-	struct elf_info elf_info;
+	struct kexec_elf_info elf_info;
 	int ret;
 
-	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
+	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
 	if (ret)
 		return ret;
 
-	elf_free_info(&elf_info);
+	kexec_free_elf_info(&elf_info);
 
 	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
 }
 
 /**
- * elf_exec_load - load ELF executable image
+ * kexec_elf_load - load ELF executable image
  * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
  *			section will be loaded in memory.
  *
  * Return:
  * 0 on success, negative value on failure.
  */
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
-			 struct elf_info *elf_info,
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info,
+			 struct kexec_buf *kbuf,
 			 unsigned long *lowest_load_addr)
 {
 	unsigned long base = 0, lowest_addr = UINT_MAX;
 	int ret;
 	size_t i;
-	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
-				  .top_down = false };
 
 	/* Read in the PT_LOAD segments. */
 	for (i = 0; i < ehdr->e_phnum; i++) {
@@ -542,15 +511,15 @@ static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
 		if (size > phdr->p_memsz)
 			size = phdr->p_memsz;
 
-		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
-		kbuf.bufsz = size;
-		kbuf.memsz = phdr->p_memsz;
-		kbuf.buf_align = phdr->p_align;
-		kbuf.buf_min = phdr->p_paddr + base;
-		ret = kexec_add_buffer(&kbuf);
+		kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
+		kbuf->bufsz = size;
+		kbuf->memsz = phdr->p_memsz;
+		kbuf->buf_align = phdr->p_align;
+		kbuf->buf_min = phdr->p_paddr + base;
+		ret = kexec_add_buffer(kbuf);
 		if (ret)
 			goto out;
-		load_addr = kbuf.mem;
+		load_addr = kbuf->mem;
 
 		if (load_addr < lowest_addr)
 			lowest_addr = load_addr;
@@ -565,100 +534,4 @@ static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
 	return ret;
 }
 
-static void *elf64_load(struct kimage *image, char *kernel_buf,
-			unsigned long kernel_len, char *initrd,
-			unsigned long initrd_len, char *cmdline,
-			unsigned long cmdline_len)
-{
-	int ret;
-	unsigned int fdt_size;
-	unsigned long kernel_load_addr;
-	unsigned long initrd_load_addr = 0, fdt_load_addr;
-	void *fdt;
-	const void *slave_code;
-	struct elfhdr ehdr;
-	struct elf_info elf_info;
-	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
-				  .buf_max = ppc64_rma_size };
-	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
-				  .buf_max = ppc64_rma_size, .top_down = true };
-
-	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
-	if (ret)
-		goto out;
-
-	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
-	if (ret)
-		goto out;
-
-	pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr);
-
-	ret = kexec_load_purgatory(image, &pbuf);
-	if (ret) {
-		pr_err("Loading purgatory failed.\n");
-		goto out;
-	}
-
-	pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
-
-	if (initrd != NULL) {
-		kbuf.buffer = initrd;
-		kbuf.bufsz = kbuf.memsz = initrd_len;
-		kbuf.buf_align = PAGE_SIZE;
-		kbuf.top_down = false;
-		ret = kexec_add_buffer(&kbuf);
-		if (ret)
-			goto out;
-		initrd_load_addr = kbuf.mem;
-
-		pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr);
-	}
-
-	fdt_size = fdt_totalsize(initial_boot_params) * 2;
-	fdt = kmalloc(fdt_size, GFP_KERNEL);
-	if (!fdt) {
-		pr_err("Not enough memory for the device tree.\n");
-		ret = -ENOMEM;
-		goto out;
-	}
-	ret = fdt_open_into(initial_boot_params, fdt, fdt_size);
-	if (ret < 0) {
-		pr_err("Error setting up the new device tree.\n");
-		ret = -EINVAL;
-		goto out;
-	}
-
-	ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline);
-	if (ret)
-		goto out;
-
-	fdt_pack(fdt);
-
-	kbuf.buffer = fdt;
-	kbuf.bufsz = kbuf.memsz = fdt_size;
-	kbuf.buf_align = PAGE_SIZE;
-	kbuf.top_down = true;
-	ret = kexec_add_buffer(&kbuf);
-	if (ret)
-		goto out;
-	fdt_load_addr = kbuf.mem;
-
-	pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr);
-
-	slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset;
-	ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr,
-			      fdt_load_addr);
-	if (ret)
-		pr_err("Error setting up the purgatory.\n");
-
-out:
-	elf_free_info(&elf_info);
-
-	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
-	return ret ? ERR_PTR(ret) : fdt;
-}
 
-const struct kexec_file_ops kexec_elf64_ops = {
-	.probe = elf64_probe,
-	.load = elf64_load,
-};
-- 
2.20.1


^ permalink raw reply related

* [PATCH v3 2/7] kexec_elf: change order of elf_*_to_cpu() functions
From: Sven Schnelle @ 2019-07-10 14:29 UTC (permalink / raw)
  To: kexec; +Cc: Sven Schnelle, deller, linuxppc-dev
In-Reply-To: <20190710142944.2774-1-svens@stackframe.org>

Change the order to have a 64/32/16 order, no functional change.

Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
 kernel/kexec_elf.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
index 6e9f52171ede..76e7df64d715 100644
--- a/kernel/kexec_elf.c
+++ b/kernel/kexec_elf.c
@@ -31,22 +31,22 @@ static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
 	return value;
 }
 
-static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
+static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
 {
 	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le16_to_cpu(value);
+		value = le32_to_cpu(value);
 	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be16_to_cpu(value);
+		value = be32_to_cpu(value);
 
 	return value;
 }
 
-static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
+static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
 {
 	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le32_to_cpu(value);
+		value = le16_to_cpu(value);
 	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be32_to_cpu(value);
+		value = be16_to_cpu(value);
 
 	return value;
 }
-- 
2.20.1


^ permalink raw reply related

* [PATCH v3 4/7] kexec_elf: remove PURGATORY_STACK_SIZE
From: Sven Schnelle @ 2019-07-10 14:29 UTC (permalink / raw)
  To: kexec; +Cc: Sven Schnelle, deller, linuxppc-dev
In-Reply-To: <20190710142944.2774-1-svens@stackframe.org>

It's not used anywhere so just drop it.

Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
 kernel/kexec_elf.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
index effe9dc0b055..70d31b8feeae 100644
--- a/kernel/kexec_elf.c
+++ b/kernel/kexec_elf.c
@@ -8,8 +8,6 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 
-#define PURGATORY_STACK_SIZE	(16 * 1024)
-
 #define elf_addr_to_cpu	elf64_to_cpu
 
 #ifndef Elf_Rel
-- 
2.20.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox