mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-12 13:34:10 +08:00
bd67b711bf
BMIPS is one of the few platforms that do change the exception base. After commit2dcb396454
("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. Additional we reserve the first 4k (1k for R3k) for either normal exception vector space (legacy CPUs) or special vectors like cache exceptions. Huge thanks to Serge for analysing and proposing a solution to this issue. Fixes:2dcb396454
("memblock: do not start bottom-up allocations with kernel_end") Reported-by: Kamal Dasu <kdasu.kdev@gmail.com> Debugged-by: Serge Semin <Sergey.Semin@baikalelectronics.ru> Acked-by: Mike Rapoport <rppt@linux.ibm.com> Tested-by: Florian Fainelli <f.fainelli@gmail.com> Reviewed-by: Serge Semin <fancer.lancer@gmail.com> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
175 lines
4.1 KiB
C
175 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Processor capabilities determination functions.
|
|
*
|
|
* Copyright (C) xxxx the Anonymous
|
|
* Copyright (C) 1994 - 2006 Ralf Baechle
|
|
* Copyright (C) 2003, 2004 Maciej W. Rozycki
|
|
* Copyright (C) 2001, 2004, 2011, 2012 MIPS Technologies, Inc.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/export.h>
|
|
|
|
#include <asm/bugs.h>
|
|
#include <asm/cpu.h>
|
|
#include <asm/cpu-features.h>
|
|
#include <asm/cpu-type.h>
|
|
#include <asm/fpu.h>
|
|
#include <asm/mipsregs.h>
|
|
#include <asm/elf.h>
|
|
#include <asm/traps.h>
|
|
|
|
#include "fpu-probe.h"
|
|
|
|
/* Hardware capabilities */
|
|
unsigned int elf_hwcap __read_mostly;
|
|
EXPORT_SYMBOL_GPL(elf_hwcap);
|
|
|
|
void __init check_bugs32(void)
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
* Probe whether cpu has config register by trying to play with
|
|
* alternate cache bit and see whether it matters.
|
|
* It's used by cpu_probe to distinguish between R3000A and R3081.
|
|
*/
|
|
static inline int cpu_has_confreg(void)
|
|
{
|
|
#ifdef CONFIG_CPU_R3000
|
|
extern unsigned long r3k_cache_size(unsigned long);
|
|
unsigned long size1, size2;
|
|
unsigned long cfg = read_c0_conf();
|
|
|
|
size1 = r3k_cache_size(ST0_ISC);
|
|
write_c0_conf(cfg ^ R30XX_CONF_AC);
|
|
size2 = r3k_cache_size(ST0_ISC);
|
|
write_c0_conf(cfg);
|
|
return size1 != size2;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static inline void set_elf_platform(int cpu, const char *plat)
|
|
{
|
|
if (cpu == 0)
|
|
__elf_platform = plat;
|
|
}
|
|
|
|
const char *__cpu_name[NR_CPUS];
|
|
const char *__elf_platform;
|
|
const char *__elf_base_platform;
|
|
|
|
void cpu_probe(void)
|
|
{
|
|
struct cpuinfo_mips *c = ¤t_cpu_data;
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
/*
|
|
* Set a default elf platform, cpu probe may later
|
|
* overwrite it with a more precise value
|
|
*/
|
|
set_elf_platform(cpu, "mips");
|
|
|
|
c->processor_id = PRID_IMP_UNKNOWN;
|
|
c->fpu_id = FPIR_IMP_NONE;
|
|
c->cputype = CPU_UNKNOWN;
|
|
c->writecombine = _CACHE_UNCACHED;
|
|
|
|
c->fpu_csr31 = FPU_CSR_RN;
|
|
c->fpu_msk31 = FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008 |
|
|
FPU_CSR_CONDX | FPU_CSR_FS;
|
|
|
|
c->srsets = 1;
|
|
|
|
c->processor_id = read_c0_prid();
|
|
switch (c->processor_id & (PRID_COMP_MASK | PRID_IMP_MASK)) {
|
|
case PRID_COMP_LEGACY | PRID_IMP_R2000:
|
|
c->cputype = CPU_R2000;
|
|
__cpu_name[cpu] = "R2000";
|
|
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
|
|
MIPS_CPU_NOFPUEX;
|
|
if (__cpu_has_fpu())
|
|
c->options |= MIPS_CPU_FPU;
|
|
c->tlbsize = 64;
|
|
break;
|
|
case PRID_COMP_LEGACY | PRID_IMP_R3000:
|
|
if ((c->processor_id & PRID_REV_MASK) == PRID_REV_R3000A) {
|
|
if (cpu_has_confreg()) {
|
|
c->cputype = CPU_R3081E;
|
|
__cpu_name[cpu] = "R3081";
|
|
} else {
|
|
c->cputype = CPU_R3000A;
|
|
__cpu_name[cpu] = "R3000A";
|
|
}
|
|
} else {
|
|
c->cputype = CPU_R3000;
|
|
__cpu_name[cpu] = "R3000";
|
|
}
|
|
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
|
|
MIPS_CPU_NOFPUEX;
|
|
if (__cpu_has_fpu())
|
|
c->options |= MIPS_CPU_FPU;
|
|
c->tlbsize = 64;
|
|
break;
|
|
case PRID_COMP_LEGACY | PRID_IMP_TX39:
|
|
c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;
|
|
|
|
if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) {
|
|
c->cputype = CPU_TX3927;
|
|
__cpu_name[cpu] = "TX3927";
|
|
c->tlbsize = 64;
|
|
} else {
|
|
switch (c->processor_id & PRID_REV_MASK) {
|
|
case PRID_REV_TX3912:
|
|
c->cputype = CPU_TX3912;
|
|
__cpu_name[cpu] = "TX3912";
|
|
c->tlbsize = 32;
|
|
break;
|
|
case PRID_REV_TX3922:
|
|
c->cputype = CPU_TX3922;
|
|
__cpu_name[cpu] = "TX3922";
|
|
c->tlbsize = 64;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
BUG_ON(!__cpu_name[cpu]);
|
|
BUG_ON(c->cputype == CPU_UNKNOWN);
|
|
|
|
/*
|
|
* Platform code can force the cpu type to optimize code
|
|
* generation. In that case be sure the cpu type is correctly
|
|
* manually setup otherwise it could trigger some nasty bugs.
|
|
*/
|
|
BUG_ON(current_cpu_type() != c->cputype);
|
|
|
|
if (mips_fpu_disabled)
|
|
c->options &= ~MIPS_CPU_FPU;
|
|
|
|
if (c->options & MIPS_CPU_FPU)
|
|
cpu_set_fpu_opts(c);
|
|
else
|
|
cpu_set_nofpu_opts(c);
|
|
|
|
reserve_exception_space(0, 0x400);
|
|
}
|
|
|
|
void cpu_report(void)
|
|
{
|
|
struct cpuinfo_mips *c = ¤t_cpu_data;
|
|
|
|
pr_info("CPU%d revision is: %08x (%s)\n",
|
|
smp_processor_id(), c->processor_id, cpu_name_string());
|
|
if (c->options & MIPS_CPU_FPU)
|
|
pr_info("FPU revision is: %08x\n", c->fpu_id);
|
|
}
|