Re: [PATCH] MIPS: kernel: Reserve exception base early to prevent corruption

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/3/21 1:14 PM, Serge Semin wrote:
> Hello  Thomas,
> Thanks for the patch. My comments are below.
> 
> On Wed, Mar 03, 2021 at 07:57:13PM +0100, Thomas Bogendoerfer wrote:
>> BMIPS is one of the few platforms that do change the exception base.
>> After commit 2dcb39645441 ("memblock: do not start bottom-up allocations
>> with kernel_end") we started seeing BMIPS boards fail to boot with the
>> built-in FDT being corrupted.
>>
>> Before the cited commit, early allocations would be in the [kernel_end,
>> RAM_END] range, but after commit they would be within [RAM_START +
>> PAGE_SIZE, RAM_END].
>>
>> The custom exception base handler that is installed by
>> bmips_ebase_setup() done for BMIPS5000 CPUs ends-up trampling on the
>> memory region allocated by unflatten_and_copy_device_tree() thus
>> corrupting the FDT used by the kernel.
>>
>> To fix this, we need to perform an early reservation of the custom
>> exception space. So we reserve it already in cpu_probe() for the CPUs
>> where this is fixed. For CPU with an ebase config register allocation
>> of exception space will be done in trap_init().
>>
>> Huge thanks to Serget for analysing and proposing a solution to this
>> issue.
>>
> 
>> Fixes: Fixes: 2dcb39645441 ("memblock: do not start bottom-up allocations with kernel_end")
> 
> Fixes tag is used twice.
> 
>> Debugged-by: Serge Semin <Sergey.Semin@xxxxxxxxxxxxxxxxxxxx>
>> Reported-by: Kamal Dasu <kdasu.kdev@xxxxxxxxx>
> 
> I'd switch these tags order. First it was reported, then the
> problem was debugged. I suppose it would be also nice to add
> Florian under the second Reported-by tag if he doesn't mind. I haven't
> seen any Kamal' email message, but a report posted by Florian only.

Kamal reported it to me privately and then I brought it publicly, still
wanted to give him credit.

> 
>> Signed-off-by: Thomas Bogendoerfer <tsbogend@xxxxxxxxxxxxxxxx>
>> ---
>>  arch/mips/include/asm/traps.h    |  4 ++++
>>  arch/mips/kernel/cpu-probe.c     |  7 +++++++
>>  arch/mips/kernel/cpu-r3k-probe.c |  3 +++
>>  arch/mips/kernel/smp-bmips.c     |  9 +--------
>>  arch/mips/kernel/traps.c         | 31 ++++++++++++++++---------------
>>  5 files changed, 31 insertions(+), 23 deletions(-)
>>
>> diff --git a/arch/mips/include/asm/traps.h b/arch/mips/include/asm/traps.h
>> index 6aa8f126a43d..d74d829e1655 100644
>> --- a/arch/mips/include/asm/traps.h
>> +++ b/arch/mips/include/asm/traps.h
>> @@ -24,7 +24,11 @@ extern void (*board_ebase_setup)(void);
>>  extern void (*board_cache_error_setup)(void);
>>  
>>  extern int register_nmi_notifier(struct notifier_block *nb);
>> +extern void reserve_exception_space(unsigned long addr, unsigned long size);
>>  extern char except_vec_nmi[];
>> +extern unsigned long ebase_size;
>> +
>> +#define VECTORSPACING 0x100	/* for EI/VI mode */
>>  
>>  #define nmi_notifier(fn, pri)						\
>>  ({									\
>> diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
>> index 9a89637b4ecf..effc45cbb351 100644
>> --- a/arch/mips/kernel/cpu-probe.c
>> +++ b/arch/mips/kernel/cpu-probe.c
>> @@ -26,6 +26,7 @@
>>  #include <asm/elf.h>
>>  #include <asm/pgtable-bits.h>
>>  #include <asm/spram.h>
>> +#include <asm/traps.h>
>>  #include <linux/uaccess.h>
>>  
>>  #include "fpu-probe.h"
>> @@ -1628,6 +1629,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
>>  		c->cputype = CPU_BMIPS3300;
>>  		__cpu_name[cpu] = "Broadcom BMIPS3300";
>>  		set_elf_platform(cpu, "bmips3300");
>> +		reserve_exception_space(0x80000400, VECTORSPACING * 64);
>>  		break;
>>  	case PRID_IMP_BMIPS43XX: {
>>  		int rev = c->processor_id & PRID_REV_MASK;
>> @@ -1638,6 +1640,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
>>  			__cpu_name[cpu] = "Broadcom BMIPS4380";
>>  			set_elf_platform(cpu, "bmips4380");
>>  			c->options |= MIPS_CPU_RIXI;
>> +			reserve_exception_space(0x80000400, VECTORSPACING * 64);
>>  		} else {
>>  			c->cputype = CPU_BMIPS4350;
>>  			__cpu_name[cpu] = "Broadcom BMIPS4350";
>> @@ -1654,6 +1657,7 @@ static inline void cpu_probe_broadcom(struct cpuinfo_mips *c, unsigned int cpu)
>>  			__cpu_name[cpu] = "Broadcom BMIPS5000";
>>  		set_elf_platform(cpu, "bmips5000");
>>  		c->options |= MIPS_CPU_ULRI | MIPS_CPU_RIXI;
>> +		reserve_exception_space(0x80001000, VECTORSPACING * 64);
>>  		break;
>>  	}
>>  }
>> @@ -2133,6 +2137,9 @@ void cpu_probe(void)
>>  	if (cpu == 0)
>>  		__ua_limit = ~((1ull << cpu_vmbits) - 1);
>>  #endif
>> +
>> +	if (ebase_size == 0 && !cpu_has_mips_r2_r6)
>> +		reserve_exception_space(CAC_BASE, 0x400);
>>  }
>>  
>>  void cpu_report(void)
>> diff --git a/arch/mips/kernel/cpu-r3k-probe.c b/arch/mips/kernel/cpu-r3k-probe.c
>> index abdbbe8c5a43..6e3f4c17b810 100644
>> --- a/arch/mips/kernel/cpu-r3k-probe.c
>> +++ b/arch/mips/kernel/cpu-r3k-probe.c
>> @@ -21,6 +21,7 @@
>>  #include <asm/fpu.h>
>>  #include <asm/mipsregs.h>
>>  #include <asm/elf.h>
>> +#include <asm/traps.h>
>>  
>>  #include "fpu-probe.h"
>>  
>> @@ -158,6 +159,8 @@ void cpu_probe(void)
>>  		cpu_set_fpu_opts(c);
>>  	else
>>  		cpu_set_nofpu_opts(c);
>> +
>> +	reserve_exception_space(CAC_BASE, 0x400);
>>  }
>>  
>>  void cpu_report(void)
>> diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
>> index b6ef5f7312cf..ad3f2282a65a 100644
>> --- a/arch/mips/kernel/smp-bmips.c
>> +++ b/arch/mips/kernel/smp-bmips.c
>> @@ -528,10 +528,6 @@ static void bmips_set_reset_vec(int cpu, u32 val)
>>  
>>  void bmips_ebase_setup(void)
>>  {
>> -	unsigned long new_ebase = ebase;
>> -
>> -	BUG_ON(ebase != CKSEG0);
>> -
>>  	switch (current_cpu_type()) {
>>  	case CPU_BMIPS4350:
>>  		/*
> 
>> @@ -554,7 +550,6 @@ void bmips_ebase_setup(void)
>>  		 * 0x8000_0000: reset/NMI (initially in kseg1)
>>  		 * 0x8000_0400: normal vectors
>>  		 */
>> -		new_ebase = 0x80000400;
>>  		bmips_set_reset_vec(0, RESET_FROM_KSEG0);
>>  		break;
>>  	case CPU_BMIPS5000:
>> @@ -562,16 +557,14 @@ void bmips_ebase_setup(void)
>>  		 * 0x8000_0000: reset/NMI (initially in kseg1)
>>  		 * 0x8000_1000: normal vectors
>>  		 */
>> -		new_ebase = 0x80001000;
>>  		bmips_set_reset_vec(0, RESET_FROM_KSEG0);
>> -		write_c0_ebase(new_ebase);
>> +		write_c0_ebase(ebase);
>>  		break;
>>  	default:
>>  		return;
>>  	}
>>  
>>  	board_nmi_handler_setup = &bmips_nmi_handler_setup;
> 
> I've just realized that Broadcom MIPS actually needs to reserve a
> space above 0x80000000 too. See the in-situ comment here, 0x8000_0000
> is said to be a space for reset/NMI. That space is then rewritten by
> the method bmips_nmi_handler_setup() called in trap_init(). Of course
> memblock allocates a memory starting from PAGE_SIZE so we are on safe
> side at boot-stage. At the same time memblock doesn't mark the lowest
> region as reserved. Thus we can't be sure that the buddy allocator
> won't ever try to use that physical memory. AFAICS bcm63xx
> also copies some vector to 0xa0000200.

Yes, good point, I missed that part as well.

> 
> Similar thing concerns all the platforms, which initialize the
> pointers: board_nmi_handler_setup and board_ejtag_handler_setup. All
> of them rewrite some lowest memory space with NMI/eJTAG vectors and
> most likely expects that data being left unchanged too.
> 
> I am a bit surprised we haven't got any bug report in that matter so
> far, because AFAIR the MIPS arch's stopped reserving memory below the
> kernel since bootmem allocator was removed. Anyway at least for the
> sake of consistency the lowest page should be reserved in the affected
> platforms.

My guess is that MIPS is just becoming less and less used so all of
these issues tend to be overlooked and/or harder to discover.
-- 
Florian



[Index of Archives]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux