HelenOS sources
This source file includes following definitions.
- fpu_have_coprocessor_access
- fpu_enable_coprocessor_access
- fpu_init
- fpu_setup
- handle_if_fpu_exception
- fpu_enable
- fpu_disable
- fpu_context_save
- fpu_context_restore
#include <fpu_context.h>
#include <arch.h>
#include <arch/types.h>
#include <arch/security_ext.h>
#include <arch/cp15.h>
#include <cpu.h>
#define FPSID_IMPLEMENTER(r) ((r) >> 24)
#define FPSID_SW_ONLY_FLAG (1 << 23)
#define FPSID_SUBACHITECTURE(r) (((r) >> 16) & 0x7f)
#define FPSID_PART_NUMBER(r) (((r) >> 8) & 0xff)
#define FPSID_VARIANT(r) (((r) >> 4) 0xf)
#define FPSID_REVISION(r) (((r) >> 0) 0xf)
enum {
FPU_VFPv1 = 0x00,
FPU_VFPv2_COMMONv1 = 0x01,
FPU_VFPv3_COMMONv2 = 0x02,
FPU_VFPv3_NO_COMMON = 0x3,
FPU_VFPv3_COMMONv3 = 0x4,
};
extern uint32_t fpsid_read(void);
extern uint32_t mvfr0_read(void);
enum {
FPEXC_EX_FLAG = (1 << 31),
FPEXC_ENABLED_FLAG = (1 << 30),
};
extern uint32_t fpexc_read(void);
extern void fpexc_write(uint32_t);
enum {
FPSCR_N_FLAG = (1 << 31),
FPSCR_Z_FLAG = (1 << 30),
FPSCR_C_FLAG = (1 << 29),
FPSCR_V_FLAG = (1 << 28),
FPSCR_QC_FLAG = (1 << 27),
FPSCR_AHP_FLAG = (1 << 26),
FPSCR_DN_FLAG = (1 << 25),
FPSCR_FZ_FLAG = (1 << 24),
FPSCR_ROUND_MODE_MASK = (0x3 << 22),
FPSCR_ROUND_TO_NEAREST = (0x0 << 22),
FPSCR_ROUND_TO_POS_INF = (0x1 << 22),
FPSCR_ROUND_TO_NEG_INF = (0x2 << 22),
FPSCR_ROUND_TO_ZERO = (0x3 << 22),
FPSCR_STRIDE_MASK = (0x3 << 20),
FPSCR_STRIDE_SHIFT = 20,
FPSCR_LEN_MASK = (0x7 << 16),
FPSCR_LEN_SHIFT = 16,
FPSCR_DENORMAL_EN_FLAG = (1 << 15),
FPSCR_INEXACT_EN_FLAG = (1 << 12),
FPSCR_UNDERFLOW_EN_FLAG = (1 << 11),
FPSCR_OVERFLOW_EN_FLAG = (1 << 10),
FPSCR_ZERO_DIV_EN_FLAG = (1 << 9),
FPSCR_INVALID_OP_EN_FLAG = (1 << 8),
FPSCR_DENORMAL_FLAG = (1 << 7),
FPSCR_INEXACT_FLAG = (1 << 4),
FPSCR_UNDERFLOW_FLAG = (1 << 3),
FPSCR_OVERLOW_FLAG = (1 << 2),
FPSCR_DIV_ZERO_FLAG = (1 << 1),
FPSCR_INVALID_OP_FLAG = (1 << 0),
FPSCR_EN_ALL = FPSCR_DENORMAL_EN_FLAG | FPSCR_INEXACT_EN_FLAG | FPSCR_UNDERFLOW_EN_FLAG | FPSCR_OVERFLOW_EN_FLAG | FPSCR_ZERO_DIV_EN_FLAG | FPSCR_INVALID_OP_EN_FLAG,
};
extern uint32_t fpscr_read(void);
extern void fpscr_write(uint32_t);
extern void fpu_context_save_s32(fpu_context_t *);
extern void fpu_context_restore_s32(fpu_context_t *);
extern void fpu_context_save_d16(fpu_context_t *);
extern void fpu_context_restore_d16(fpu_context_t *);
extern void fpu_context_save_d32(fpu_context_t *);
extern void fpu_context_restore_d32(fpu_context_t *);
static void (*save_context)(fpu_context_t *ctx);
static void (*restore_context)(fpu_context_t *ctx);
static int fpu_have_coprocessor_access(void)
{
#ifdef PROCESSOR_ARCH_armv7_a
const uint32_t cpacr = CPACR_read();
if (((cpacr & CPACR_CP_MASK(10)) != CPACR_CP_FULL_ACCESS(10)) &&
((cpacr & CPACR_CP_MASK(11)) != CPACR_CP_FULL_ACCESS(11))) {
printf("No access to CP10 and CP11: %" PRIx32 "\n", cpacr);
return 0;
}
#endif
return 1;
}
static void fpu_enable_coprocessor_access(void)
{
#ifdef PROCESSOR_ARCH_armv7_a
uint32_t cpacr = CPACR_read();
cpacr &= ~(CPACR_CP_MASK(10) | CPACR_CP_MASK(11));
cpacr |= (CPACR_CP_FULL_ACCESS(10) | CPACR_CP_FULL_ACCESS(11));
CPACR_write(cpacr);
#endif
}
void fpu_init(void)
{
if (!fpu_have_coprocessor_access())
return;
fpexc_write(0);
fpu_enable();
fpscr_write(fpscr_read() & ~FPSCR_EN_ALL);
}
void fpu_setup(void)
{
uint32_t mvfr0;
fpu_enable_coprocessor_access();
if (!fpu_have_coprocessor_access())
return;
const uint32_t fpsid = fpsid_read();
if (fpsid & FPSID_SW_ONLY_FLAG) {
printf("No FPU avaiable\n");
return;
}
switch (FPSID_SUBACHITECTURE(fpsid)) {
case FPU_VFPv1:
printf("Detected VFPv1\n");
save_context = fpu_context_save_s32;
restore_context = fpu_context_restore_s32;
break;
case FPU_VFPv2_COMMONv1:
printf("Detected VFPv2\n");
save_context = fpu_context_save_d16;
restore_context = fpu_context_restore_d16;
break;
case FPU_VFPv3_COMMONv2:
case FPU_VFPv3_NO_COMMON:
case FPU_VFPv3_COMMONv3:
mvfr0 = mvfr0_read();
if ((mvfr0 & 0xf) == 0x1) {
printf("Detected VFPv3+ with 16 regs\n");
save_context = fpu_context_save_d16;
restore_context = fpu_context_restore_d16;
} else {
printf("Detected VFPv3+ with 32 regs\n");
save_context = fpu_context_save_d32;
restore_context = fpu_context_restore_d32;
}
break;
}
}
bool handle_if_fpu_exception(void)
{
if (!fpu_have_coprocessor_access())
return false;
const uint32_t fpexc = fpexc_read();
if (fpexc & FPEXC_ENABLED_FLAG) {
const uint32_t fpscr = fpscr_read();
printf("FPU exception\n"
"\tFPEXC: %" PRIx32 " FPSCR: %" PRIx32 "\n", fpexc, fpscr);
return false;
}
#ifdef CONFIG_FPU_LAZY
scheduler_fpu_lazy_request();
return true;
#else
return false;
#endif
}
void fpu_enable(void)
{
if (!fpu_have_coprocessor_access())
return;
fpexc_write(fpexc_read() | FPEXC_ENABLED_FLAG);
}
void fpu_disable(void)
{
if (!fpu_have_coprocessor_access())
return;
fpexc_write(fpexc_read() & ~FPEXC_ENABLED_FLAG);
}
void fpu_context_save(fpu_context_t *ctx)
{
#if 0
const uint32_t fpexc = fpexc_read();
if (fpexc & FPEXC_EX_FLAG) {
printf("EX FPU flag is on, things will fail\n");
}
#endif
if (save_context)
save_context(ctx);
}
void fpu_context_restore(fpu_context_t *ctx)
{
if (restore_context)
restore_context(ctx);
}
HelenOS homepage, sources at GitHub