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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user