arch-riscv: Update FS bits when doing floating point loads

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

Change-Id: Ia5656da5a589a8e29fb699d2ee12885b8f3fa2d2
Signed-off-by: Hoa Nguyen <hn@hnpl.org>
This commit is contained in:
Hoa Nguyen
2023-09-28 19:14:29 -07:00
parent 62d34ef374
commit 6640447c1e

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;