mips: cm: Convert to bitfield API to fix out-of-bounds access

[ Upstream commit 18b8f5b6fc53d097cadb94a93d8d6566ba88e389 ]

mips_cm_error_report() extracts the cause and other cause from the error
register using shifts.  This works fine for the former, as it is stored
in the top bits, and the shift will thus remove all non-related bits.
However, the latter is stored in the bottom bits, hence thus needs masking
to get rid of non-related bits.  Without such masking, using it as an
index into the cm2_causes[] array will lead to an out-of-bounds access,
probably causing a crash.

Fix this by using FIELD_GET() instead.  Bite the bullet and convert all
MIPS CM handling to the bitfield API, to improve readability and safety.

Fixes: 3885c2b463 ("MIPS: CM: Add support for reporting CM cache errors")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Geert Uytterhoeven 2021-10-29 11:58:16 +02:00 committed by Greg Kroah-Hartman
parent fb7b73cd94
commit 3f698d97f7
2 changed files with 16 additions and 17 deletions

View File

@ -11,6 +11,7 @@
#ifndef __MIPS_ASM_MIPS_CM_H__ #ifndef __MIPS_ASM_MIPS_CM_H__
#define __MIPS_ASM_MIPS_CM_H__ #define __MIPS_ASM_MIPS_CM_H__
#include <linux/bitfield.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/errno.h> #include <linux/errno.h>
@ -153,8 +154,8 @@ GCR_ACCESSOR_RO(32, 0x030, rev)
#define CM_GCR_REV_MINOR GENMASK(7, 0) #define CM_GCR_REV_MINOR GENMASK(7, 0)
#define CM_ENCODE_REV(major, minor) \ #define CM_ENCODE_REV(major, minor) \
(((major) << __ffs(CM_GCR_REV_MAJOR)) | \ (FIELD_PREP(CM_GCR_REV_MAJOR, major) | \
((minor) << __ffs(CM_GCR_REV_MINOR))) FIELD_PREP(CM_GCR_REV_MINOR, minor))
#define CM_REV_CM2 CM_ENCODE_REV(6, 0) #define CM_REV_CM2 CM_ENCODE_REV(6, 0)
#define CM_REV_CM2_5 CM_ENCODE_REV(7, 0) #define CM_REV_CM2_5 CM_ENCODE_REV(7, 0)
@ -362,10 +363,10 @@ static inline int mips_cm_revision(void)
static inline unsigned int mips_cm_max_vp_width(void) static inline unsigned int mips_cm_max_vp_width(void)
{ {
extern int smp_num_siblings; extern int smp_num_siblings;
uint32_t cfg;
if (mips_cm_revision() >= CM_REV_CM3) if (mips_cm_revision() >= CM_REV_CM3)
return read_gcr_sys_config2() & CM_GCR_SYS_CONFIG2_MAXVPW; return FIELD_GET(CM_GCR_SYS_CONFIG2_MAXVPW,
read_gcr_sys_config2());
if (mips_cm_present()) { if (mips_cm_present()) {
/* /*
@ -373,8 +374,7 @@ static inline unsigned int mips_cm_max_vp_width(void)
* number of VP(E)s, and if that ever changes then this will * number of VP(E)s, and if that ever changes then this will
* need revisiting. * need revisiting.
*/ */
cfg = read_gcr_cl_config() & CM_GCR_Cx_CONFIG_PVPE; return FIELD_GET(CM_GCR_Cx_CONFIG_PVPE, read_gcr_cl_config()) + 1;
return (cfg >> __ffs(CM_GCR_Cx_CONFIG_PVPE)) + 1;
} }
if (IS_ENABLED(CONFIG_SMP)) if (IS_ENABLED(CONFIG_SMP))

View File

@ -179,8 +179,7 @@ static void mips_cm_probe_l2sync(void)
phys_addr_t addr; phys_addr_t addr;
/* L2-only sync was introduced with CM major revision 6 */ /* L2-only sync was introduced with CM major revision 6 */
major_rev = (read_gcr_rev() & CM_GCR_REV_MAJOR) >> major_rev = FIELD_GET(CM_GCR_REV_MAJOR, read_gcr_rev());
__ffs(CM_GCR_REV_MAJOR);
if (major_rev < 6) if (major_rev < 6)
return; return;
@ -263,13 +262,13 @@ void mips_cm_lock_other(unsigned int cluster, unsigned int core,
preempt_disable(); preempt_disable();
if (cm_rev >= CM_REV_CM3) { if (cm_rev >= CM_REV_CM3) {
val = core << __ffs(CM3_GCR_Cx_OTHER_CORE); val = FIELD_PREP(CM3_GCR_Cx_OTHER_CORE, core) |
val |= vp << __ffs(CM3_GCR_Cx_OTHER_VP); FIELD_PREP(CM3_GCR_Cx_OTHER_VP, vp);
if (cm_rev >= CM_REV_CM3_5) { if (cm_rev >= CM_REV_CM3_5) {
val |= CM_GCR_Cx_OTHER_CLUSTER_EN; val |= CM_GCR_Cx_OTHER_CLUSTER_EN;
val |= cluster << __ffs(CM_GCR_Cx_OTHER_CLUSTER); val |= FIELD_PREP(CM_GCR_Cx_OTHER_CLUSTER, cluster);
val |= block << __ffs(CM_GCR_Cx_OTHER_BLOCK); val |= FIELD_PREP(CM_GCR_Cx_OTHER_BLOCK, block);
} else { } else {
WARN_ON(cluster != 0); WARN_ON(cluster != 0);
WARN_ON(block != CM_GCR_Cx_OTHER_BLOCK_LOCAL); WARN_ON(block != CM_GCR_Cx_OTHER_BLOCK_LOCAL);
@ -299,7 +298,7 @@ void mips_cm_lock_other(unsigned int cluster, unsigned int core,
spin_lock_irqsave(&per_cpu(cm_core_lock, curr_core), spin_lock_irqsave(&per_cpu(cm_core_lock, curr_core),
per_cpu(cm_core_lock_flags, curr_core)); per_cpu(cm_core_lock_flags, curr_core));
val = core << __ffs(CM_GCR_Cx_OTHER_CORENUM); val = FIELD_PREP(CM_GCR_Cx_OTHER_CORENUM, core);
} }
write_gcr_cl_other(val); write_gcr_cl_other(val);
@ -343,8 +342,8 @@ void mips_cm_error_report(void)
cm_other = read_gcr_error_mult(); cm_other = read_gcr_error_mult();
if (revision < CM_REV_CM3) { /* CM2 */ if (revision < CM_REV_CM3) { /* CM2 */
cause = cm_error >> __ffs(CM_GCR_ERROR_CAUSE_ERRTYPE); cause = FIELD_GET(CM_GCR_ERROR_CAUSE_ERRTYPE, cm_error);
ocause = cm_other >> __ffs(CM_GCR_ERROR_MULT_ERR2ND); ocause = FIELD_GET(CM_GCR_ERROR_MULT_ERR2ND, cm_other);
if (!cause) if (!cause)
return; return;
@ -386,8 +385,8 @@ void mips_cm_error_report(void)
ulong core_id_bits, vp_id_bits, cmd_bits, cmd_group_bits; ulong core_id_bits, vp_id_bits, cmd_bits, cmd_group_bits;
ulong cm3_cca_bits, mcp_bits, cm3_tr_bits, sched_bit; ulong cm3_cca_bits, mcp_bits, cm3_tr_bits, sched_bit;
cause = cm_error >> __ffs64(CM3_GCR_ERROR_CAUSE_ERRTYPE); cause = FIELD_GET(CM3_GCR_ERROR_CAUSE_ERRTYPE, cm_error);
ocause = cm_other >> __ffs(CM_GCR_ERROR_MULT_ERR2ND); ocause = FIELD_GET(CM_GCR_ERROR_MULT_ERR2ND, cm_other);
if (!cause) if (!cause)
return; return;