RISC-V: Fix IPI/RFENCE hmask on non-monotonic hartid ordering

If the boot CPU does not have the lowest hartid, "hartid - hbase" can
become negative, leading to an incorrect hmask, causing userspace to
crash with SEGV. This is observed on e.g. Starlight Beta, where cpuid 1
maps to hartid 0, and cpuid 0 maps to hartid 1.

Fix this by detecting this case, and shifting the accumulated mask and
updating hbase, if possible.

Fixes: 26fb751ca37846c9 ("RISC-V: Do not use cpumask data structure for hartid bitmap")
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Reviewed-by: Atish Patra <atishp@rivosinc.com>
Tested-by: Atish Patra <atishp@rivosinc.com>
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>

authored by

Geert Uytterhoeven and committed by
Palmer Dabbelt
5feef64f 2b35d5b7

+39 -18
+39 -18
arch/riscv/kernel/sbi.c
··· 250 250 251 251 static int __sbi_send_ipi_v02(const struct cpumask *cpu_mask) 252 252 { 253 - unsigned long hartid, cpuid, hmask = 0, hbase = 0; 253 + unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0; 254 254 struct sbiret ret = {0}; 255 255 int result; 256 256 ··· 259 259 260 260 for_each_cpu(cpuid, cpu_mask) { 261 261 hartid = cpuid_to_hartid_map(cpuid); 262 - if (hmask && ((hbase + BITS_PER_LONG) <= hartid)) { 263 - ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, 264 - hmask, hbase, 0, 0, 0, 0); 265 - if (ret.error) 266 - goto ecall_failed; 267 - hmask = 0; 268 - hbase = 0; 262 + if (hmask) { 263 + if (hartid + BITS_PER_LONG <= htop || 264 + hbase + BITS_PER_LONG <= hartid) { 265 + ret = sbi_ecall(SBI_EXT_IPI, 266 + SBI_EXT_IPI_SEND_IPI, hmask, 267 + hbase, 0, 0, 0, 0); 268 + if (ret.error) 269 + goto ecall_failed; 270 + hmask = 0; 271 + } else if (hartid < hbase) { 272 + /* shift the mask to fit lower hartid */ 273 + hmask <<= hbase - hartid; 274 + hbase = hartid; 275 + } 269 276 } 270 - if (!hmask) 277 + if (!hmask) { 271 278 hbase = hartid; 279 + htop = hartid; 280 + } else if (hartid > htop) { 281 + htop = hartid; 282 + } 272 283 hmask |= BIT(hartid - hbase); 273 284 } 274 285 ··· 356 345 unsigned long start, unsigned long size, 357 346 unsigned long arg4, unsigned long arg5) 358 347 { 359 - unsigned long hartid, cpuid, hmask = 0, hbase = 0; 348 + unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0; 360 349 int result; 361 350 362 351 if (!cpu_mask || cpumask_empty(cpu_mask)) ··· 364 353 365 354 for_each_cpu(cpuid, cpu_mask) { 366 355 hartid = cpuid_to_hartid_map(cpuid); 367 - if (hmask && ((hbase + BITS_PER_LONG) <= hartid)) { 368 - result = __sbi_rfence_v02_call(fid, hmask, hbase, 369 - start, size, arg4, arg5); 370 - if (result) 371 - return result; 372 - hmask = 0; 373 - hbase = 0; 356 + if (hmask) { 357 + if (hartid + BITS_PER_LONG <= htop || 358 + hbase + BITS_PER_LONG <= hartid) { 359 + result = __sbi_rfence_v02_call(fid, hmask, 360 + hbase, start, size, arg4, arg5); 361 + if (result) 362 + return result; 363 + hmask = 0; 364 + } else if (hartid < hbase) { 365 + /* shift the mask to fit lower hartid */ 366 + hmask <<= hbase - hartid; 367 + hbase = hartid; 368 + } 374 369 } 375 - if (!hmask) 370 + if (!hmask) { 376 371 hbase = hartid; 372 + htop = hartid; 373 + } else if (hartid > htop) { 374 + htop = hartid; 375 + } 377 376 hmask |= BIT(hartid - hbase); 378 377 } 379 378