arch-riscv: Update FS bits when doing floating point loads (#370)

This problem is similar to the problem described in [1]. This problem
produces symptoms as described in [2].

In short, the Linux kernel relies on the CSR_STATUS's FS bits to decide
whether to save the floating point registers. If the FS bits are set to
DIRTY, the floating point registers will be saved during context
switching / task switching.

Currently, with the patch in [1], we only change the FS bits upon every
floating arithmetic instruction. However, since floating load
instructions also mutate the state of floating point registers, the FS
bits should be updated to DIRTY.

The problem in [2] arose when the program populates the content of one
floating register to an array by repeatedly using `fld fa5, EA`. A
context switch occured upon a page fault, and while handling that page
fault, the kernel might have to handle an interrupt. This caused the
kernel to task switch between handling page fault and handling
interrupt. This caused __switch_to() to be called, which will save the
floating point registers only if the SD (indirectly set by FS) bits are
set to DIRTY, while restoring the floating point registers to the
switch-to task [3]. This caused the floating point registers to be
zeroed out when it was restored as it was never saved before.

[1] https://gem5-review.googlesource.com/c/public/gem5/+/65272
[2] https://github.com/gem5/gem5/issues/349
[3]
https://github.com/torvalds/linux/blob/v6.5/arch/riscv/include/asm/switch_to.h#L56
This commit is contained in:
Bobby R. Bruce
2023-09-29 10:47:05 -07:00
committed by GitHub

View File

@@ -61,6 +61,11 @@ decode QUADRANT default Unknown::unknown() {
return std::make_shared<IllegalInstFault>("FPU is off",
machInst);
// Mutating any floating point register changes the FS bit
// of the STATUS CSR.
status.fs = FPUStatus::DIRTY;
xc->setMiscReg(MISCREG_STATUS, status);
Fp2_bits = Mem;
}}, {{
EA = rvZext(Rp1 + offset);
@@ -312,6 +317,11 @@ decode QUADRANT default Unknown::unknown() {
return std::make_shared<IllegalInstFault>("FPU is off",
machInst);
// Mutating any floating point register changes the FS bit
// of the STATUS CSR.
status.fs = FPUStatus::DIRTY;
xc->setMiscReg(MISCREG_STATUS, status);
Fc1_bits = Mem;
}}, {{
EA = rvZext(sp + offset);
@@ -495,6 +505,12 @@ decode QUADRANT default Unknown::unknown() {
if (status.fs == FPUStatus::OFF)
return std::make_shared<IllegalInstFault>(
"FPU is off", machInst);
// Mutating any floating point register changes the FS bit
// of the STATUS CSR.
status.fs = FPUStatus::DIRTY;
xc->setMiscReg(MISCREG_STATUS, status);
freg_t fd;
fd = freg(f64(Mem));
Fd_bits = fd.v;