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

From: Florian Fainelli
Date: Wed Mar 03 2021 - 19:12:04 EST


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