blob: 5a55a3ba6f03c50a25ca0520868edd5634908c3d [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "arch/riscv64/fpu.h"
#include <stdint.h>
#include <kernel/thread.h>
// Save the current on-cpu fpu state to the thread. Takes as an argument
// the current HW status in the sstatus register.
void riscv64_thread_fpu_save(Thread* thread, Riscv64FpuStatus fpu_status) {
switch (fpu_status) {
case Riscv64FpuStatus::DIRTY:
// The hardware state is dirty, save the old state.
riscv64_fpu_save(&thread->arch().fpu_state);
// Record that this thread has modified the state and will need to restore it
// from now on out on every context switch.
thread->arch().fpu_dirty = true;
break;
// These three states means the thread didn't modify the state, so do not
// write anything back.
case Riscv64FpuStatus::INITIAL:
// The old thread has the initial zeroed state.
case Riscv64FpuStatus::CLEAN:
// The old thread has some valid state loaded, but it didn't modify it.
break;
case Riscv64FpuStatus::OFF:
// We currently leave the fpu on all the time, so we should never get this state.
// If lazy FPU load is implemented, this will be a valid state if the thread has
// never trapped.
panic("riscv context switch: FPU was disabled during context switch: sstatus.fs %#lx\n",
static_cast<unsigned long>(fpu_status));
}
}
// Restores the fpu state to hardware from the thread passed in. current_state_initial
// should hold whether or not the sstatus.fs bits are currently RISCV64_CSR_SSTATUS_FS_INITIAL,
// as an optimization to avoid re-reading sstatus any more than necessary.
void riscv64_thread_fpu_restore(const Thread* t, Riscv64FpuStatus fpu_status) {
// Restore the state from the new thread
if (t->arch().fpu_dirty) {
riscv64_fpu_restore(&t->arch().fpu_state);
// Set the fpu hardware state to clean
riscv64_csr_clear(RISCV64_CSR_SSTATUS, RISCV64_CSR_SSTATUS_FS_MASK);
riscv64_csr_set(RISCV64_CSR_SSTATUS, RISCV64_CSR_SSTATUS_FS_CLEAN);
} else if (fpu_status != Riscv64FpuStatus::INITIAL) {
// Zero the state of the fpu unit if it currently is known to have something other
// than the initial state loaded.
riscv64_fpu_zero();
// Set the fpu hardware state to clean
riscv64_csr_clear(RISCV64_CSR_SSTATUS, RISCV64_CSR_SSTATUS_FS_MASK);
riscv64_csr_set(RISCV64_CSR_SSTATUS, RISCV64_CSR_SSTATUS_FS_INITIAL);
} else { // thread does not have dirty state and fpu state == INITIAL
// The old fpu hardware should have the initial state here and we didn't reset it
// so we should still be in the initial state.
DEBUG_ASSERT(fpu_status == Riscv64FpuStatus::INITIAL);
}
}