/* * \brief ARM-specific FPU driver for core * \author Stefan Kalkowski * \author Martin stein * \date 2016-01-19 */ /* * Copyright (C) 2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _SPEC__ARM__FPU_H_ #define _SPEC__ARM__FPU_H_ #include namespace Genode { class Fpu; } /** * FPU driver for the ARM VFPv3-D16 architecture */ class Genode::Fpu { private: /** * Floating-point Status and Control Register */ struct Fpscr : Register<32> { /** * Read register value */ static access_t read() { /* FIXME: See annotation 1. */ access_t v; asm volatile ("mrc p10, 7, %[v], cr1, cr0, 0" : [v] "=r" (v) ::); return v; } /** * Override register value * * \param v write value */ static void write(access_t const v) { /* FIXME: See annotation 1. */ asm volatile ("mcr p10, 7, %[v], cr1, cr0, 0" :: [v] "r" (v) :); } }; /** * Floating-Point Exception Control register */ struct Fpexc : Register<32> { struct En : Bitfield<30, 1> { }; /** * Read register value */ static access_t read() { /* FIXME: See annotation 1. */ access_t v; asm volatile ("mrc p10, 7, %[v], cr8, cr0, 0" : [v] "=r" (v) ::); return v; } /** * Override register value * * \param v write value */ static void write(access_t const v) { /* FIXME: See annotation 1. */ asm volatile ("mcr p10, 7, %[v], cr8, cr0, 0" :: [v] "r" (v) :); } }; public: class Context { private: friend class Fpu; /* advanced FP/SIMD - system registers */ uint32_t fpscr; uint32_t fpexc; /* advanced FP/SIMD - general purpose registers d0-d15 */ uint64_t d0, d1, d2, d3, d4, d5, d6, d7; uint64_t d8, d9, d10, d11, d12, d13, d14, d15; Fpu * _fpu = nullptr; public: Context() : fpexc(Fpexc::En::bits(1)) { } ~Context() { if (_fpu) _fpu->unset(*this); } }; private: Context * _context = nullptr; /** * Enable FPU */ void _enable() { Fpexc::access_t fpexc = Fpexc::read(); Fpexc::En::set(fpexc, 1); Fpexc::write(fpexc); } /** * Disable FPU */ void _disable() { Fpexc::access_t fpexc = Fpexc::read(); Fpexc::En::set(fpexc, 0); Fpexc::write(fpexc); } /** * Save FPU context */ void _save() { /* save system registers */ _context->fpexc = Fpexc::read(); _context->fpscr = Fpscr::read(); /* * Save D0 - D15 * * FIXME: See annotation 2. */ void * const d0_d15_base = &_context->d0; asm volatile ( "stc p11, cr0, [%[d0_d15_base]], #128" :: [d0_d15_base] "r" (d0_d15_base) : ); } /** * Load context to FPU */ void _load() { /* load system registers */ Fpexc::write(_context->fpexc); Fpscr::write(_context->fpscr); /* * Load D0 - D15 * * FIXME: See annotation 2. */ void * const d0_d15_base = &_context->d0; asm volatile ( "ldc p11, cr0, [%[d0_d15_base]], #128" :: [d0_d15_base] "r" (d0_d15_base) : ); } /** * Return wether the FPU is enabled */ bool _enabled() { Fpexc::access_t fpexc = Fpexc::read(); return Fpexc::En::get(fpexc); } public: /** * Initialize FPU */ void init(); void switch_to(Context & context) { if (_context == &context) return; _disable(); } /** * Return wether the FPU fault can be solved * * \param state CPU state of the user */ bool fault(Context & context) { if (_enabled()) { return false; } _enable(); if (_context != &context) { if (_context) { _save(); _context->_fpu = nullptr; } _context = &context; _context->_fpu = this; _load(); } return true; } /** * Unset FPU context */ void unset(Context &context) { if (_context == &context) _context = nullptr; } }; /* * Annotation 1 * * According to the ARMv7 manual this should be done via vmsr/vmrs instruction * but it seems that binutils 2.22 doesn't fully support this yet. Hence, we * use a co-processor instruction instead. The parameters to target the * register this way can be determined via 'sys/arm/include/vfp.h' and * 'sys/arm/arm/vfp.c' of the FreeBSD head branch as from 2014.04.17. */ /* * Annotation 2 * * According to the ARMv7 manual this should be done via vldm/vstm instruction * but it seems that binutils 2.22 doesn't fully support this yet. Hence, we * use a co-processor instruction instead. The parameters to target the * register this way can be determined via 'sys/arm/arm/vfp.c' of the FreeBSD * head branch as from 2014.04.17. */ #endif /* _SPEC__ARM__FPU_H_ */