hw_x86_64: Enable FPU support
* Enable the use of the FXSAVE and FXRSTOR instructions, see Intel SDM Vol. 3C, section 2.5. * The state of the x87 floating point unit (FPU) is loaded and saved on demand. * Make the cr0 control register accessible in the Cpu class. This is in preparation of the upcoming FPU management. * Access to the FPU is disabled by setting the Task Switch flag in the cr0 register. * Access to the FPU is enabled by clearing the Task Switch flag in the cr0 register. * Implement FPU initialization * Add is_fpu_enabled helper function * Add pointer to CPU lazy state to CPU class * Init FPU when finishing kernel initialization * Add function to retry FPU instruction: Similar to the ARM mechanism to retry undefined instructions, implement a function for retrying an FPU instruction. If a floating-point instruction causes an #NM exception due to the FPU being disabled, it can be retried after the correct FPU state is restored, saving the current state and enabling the FPU in the process. * Disable FPU when switching to different user context: This enables lazy save/restore of the FPU since trying to execute a floating point instruction when the FPU is disabled will cause a #NM exception. * Declare constant for #NM exception * Retry FPU instruction on #NM exception * Assure alignment of FXSAVE area: The FXSAVE area is 512-byte memory region that must be 16-byte aligned. As it turns out the alignment attribute is not honored in all cases so add a workaround to assure the alignment constraint is met by manually rounding the start of the FXSAVE area to the next 16-byte boundary if necessary.
This commit is contained in:
parent
c14898703d
commit
7ce8464b3a
|
@ -31,7 +31,7 @@ namespace Genode
|
||||||
/**
|
/**
|
||||||
* Part of CPU state that is not switched on every mode transition
|
* Part of CPU state that is not switched on every mode transition
|
||||||
*/
|
*/
|
||||||
class Cpu_lazy_state { };
|
class Cpu_lazy_state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CPU driver for core
|
* CPU driver for core
|
||||||
|
@ -41,15 +41,121 @@ namespace Genode
|
||||||
|
|
||||||
namespace Kernel { using Genode::Cpu_lazy_state; }
|
namespace Kernel { using Genode::Cpu_lazy_state; }
|
||||||
|
|
||||||
class Genode::Cpu
|
class Genode::Cpu_lazy_state
|
||||||
{
|
{
|
||||||
|
friend class Cpu;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Idt *_idt;
|
|
||||||
Tss *_tss;
|
/**
|
||||||
|
* FXSAVE area providing storage for x87 FPU, MMX, XMM, and MXCSR
|
||||||
|
* registers.
|
||||||
|
*
|
||||||
|
* For further details see Intel SDM Vol. 2A, 'FXSAVE instruction'.
|
||||||
|
*/
|
||||||
|
char fxsave_area[527];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 16-byte aligned start of FXSAVE area.
|
||||||
|
*/
|
||||||
|
char *start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load x87 FPU State from fxsave area.
|
||||||
|
*/
|
||||||
|
inline void load() {
|
||||||
|
asm volatile ("fxrstor %0" : : "m" (*start)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save x87 FPU State to fxsave area.
|
||||||
|
*/
|
||||||
|
inline void save() {
|
||||||
|
asm volatile ("fxsave %0" : "=m" (*start)); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Cpu()
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* Calculate 16-byte aligned start of FXSAVE area if necessary.
|
||||||
|
*/
|
||||||
|
inline Cpu_lazy_state()
|
||||||
|
{
|
||||||
|
start = fxsave_area;
|
||||||
|
if((addr_t)start & 15)
|
||||||
|
start = (char *)((addr_t)start & ~15) + 16;
|
||||||
|
};
|
||||||
|
} __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
|
||||||
|
class Genode::Cpu
|
||||||
|
{
|
||||||
|
friend class Cpu_lazy_state;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Idt *_idt;
|
||||||
|
Tss *_tss;
|
||||||
|
Cpu_lazy_state *_fpu_state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control register 0
|
||||||
|
*/
|
||||||
|
struct Cr0 : Register<64>
|
||||||
|
{
|
||||||
|
struct Pe : Bitfield<0, 1> { }; /* Protection Enable */
|
||||||
|
struct Mp : Bitfield<1, 1> { }; /* Monitor Coprocessor */
|
||||||
|
struct Em : Bitfield<2, 1> { }; /* Emulation */
|
||||||
|
struct Ts : Bitfield<3, 1> { }; /* Task Switched */
|
||||||
|
struct Et : Bitfield<4, 1> { }; /* Extension Type */
|
||||||
|
struct Ne : Bitfield<5, 1> { }; /* Numeric Error */
|
||||||
|
struct Wp : Bitfield<16, 1> { }; /* Write Protect */
|
||||||
|
struct Am : Bitfield<18, 1> { }; /* Alignment Mask */
|
||||||
|
struct Nw : Bitfield<29, 1> { }; /* Not Write-through */
|
||||||
|
struct Cd : Bitfield<30, 1> { }; /* Cache Disable */
|
||||||
|
struct Pg : Bitfield<31, 1> { }; /* Paging */
|
||||||
|
|
||||||
|
static void write(access_t const v) {
|
||||||
|
asm volatile ("mov %0, %%cr0" :: "r" (v) : ); }
|
||||||
|
|
||||||
|
static access_t read()
|
||||||
|
{
|
||||||
|
access_t v;
|
||||||
|
asm volatile ("mov %%cr0, %0" : "=r" (v) :: );
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable FPU by setting the TS flag in CR0.
|
||||||
|
*/
|
||||||
|
static void _disable_fpu()
|
||||||
|
{
|
||||||
|
Cr0::write(Cr0::read() | Cr0::Ts::bits(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable FPU by clearing the TS flag in CR0.
|
||||||
|
*/
|
||||||
|
static void _enable_fpu() {
|
||||||
|
asm volatile ("clts"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize FPU without checking for pending unmasked floating-point
|
||||||
|
* exceptions.
|
||||||
|
*/
|
||||||
|
static void _init_fpu() {
|
||||||
|
asm volatile ("fninit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns True if the FPU is enabled.
|
||||||
|
*/
|
||||||
|
static bool is_fpu_enabled() {
|
||||||
|
return !Cr0::Ts::get(Cr0::read()); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Cpu() : _fpu_state(0)
|
||||||
{
|
{
|
||||||
if (primary_id() == executing_id()) {
|
if (primary_id() == executing_id()) {
|
||||||
_idt = new (&_mt_idt) Idt();
|
_idt = new (&_mt_idt) Idt();
|
||||||
|
@ -259,8 +365,8 @@ class Genode::Cpu
|
||||||
init_virt_kernel(addr_t const table, unsigned const process_id) {
|
init_virt_kernel(addr_t const table, unsigned const process_id) {
|
||||||
Cr3::write(Cr3::init(table)); }
|
Cr3::write(Cr3::init(table)); }
|
||||||
|
|
||||||
inline static void finish_init_phys_kernel()
|
inline static void finish_init_phys_kernel() {
|
||||||
{ }
|
_init_fpu(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this module appropriately for the first kernel run
|
* Configure this module appropriately for the first kernel run
|
||||||
|
@ -290,6 +396,25 @@ class Genode::Cpu
|
||||||
*/
|
*/
|
||||||
bool retry_undefined_instr(Cpu_lazy_state *) { return false; }
|
bool retry_undefined_instr(Cpu_lazy_state *) { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to retry an FPU instruction after this call
|
||||||
|
*/
|
||||||
|
bool retry_fpu_instr(Cpu_lazy_state * const state)
|
||||||
|
{
|
||||||
|
if (is_fpu_enabled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_enable_fpu();
|
||||||
|
if (_fpu_state != state) {
|
||||||
|
if (_fpu_state)
|
||||||
|
_fpu_state->save();
|
||||||
|
|
||||||
|
state->load();
|
||||||
|
_fpu_state = state;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return kernel name of the executing CPU
|
* Return kernel name of the executing CPU
|
||||||
*/
|
*/
|
||||||
|
@ -300,13 +425,27 @@ class Genode::Cpu
|
||||||
*/
|
*/
|
||||||
static unsigned primary_id() { return 0; }
|
static unsigned primary_id() { return 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare for the proceeding of a user
|
||||||
|
*
|
||||||
|
* \param old_state CPU state of the last user
|
||||||
|
* \param new_state CPU state of the next user
|
||||||
|
*/
|
||||||
|
static void prepare_proceeding(Cpu_lazy_state * const old_state,
|
||||||
|
Cpu_lazy_state * const new_state)
|
||||||
|
{
|
||||||
|
if (old_state == new_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_disable_fpu();
|
||||||
|
}
|
||||||
|
|
||||||
/*************
|
/*************
|
||||||
** Dummies **
|
** Dummies **
|
||||||
*************/
|
*************/
|
||||||
|
|
||||||
static void tlb_insertions() { inval_branch_predicts(); }
|
static void tlb_insertions() { inval_branch_predicts(); }
|
||||||
static void translation_added(addr_t, size_t) { }
|
static void translation_added(addr_t, size_t) { }
|
||||||
static void prepare_proceeding(Cpu_lazy_state *, Cpu_lazy_state *) { }
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,11 @@ void Thread::exception(unsigned const cpu)
|
||||||
if (trapno == PAGE_FAULT) {
|
if (trapno == PAGE_FAULT) {
|
||||||
_mmu_exception();
|
_mmu_exception();
|
||||||
return;
|
return;
|
||||||
|
} else if (trapno == NO_MATH_COPROC) {
|
||||||
|
if (_cpu->retry_fpu_instr(&_lazy_state)) { return; }
|
||||||
|
PWRN("fpu error");
|
||||||
|
_stop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (trapno == SUPERVISOR_CALL) {
|
if (trapno == SUPERVISOR_CALL) {
|
||||||
_call();
|
_call();
|
||||||
|
|
|
@ -29,9 +29,10 @@
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
|
|
||||||
/* Enable PAE (prerequisite for IA-32e mode) */
|
/* Enable PAE (prerequisite for IA-32e mode) and OSFXSR */
|
||||||
movl %cr4, %eax
|
movl %cr4, %eax
|
||||||
btsl $5, %eax
|
btsl $5, %eax
|
||||||
|
btsl $9, %eax
|
||||||
movl %eax, %cr4
|
movl %eax, %cr4
|
||||||
|
|
||||||
/* Load initial pagetables */
|
/* Load initial pagetables */
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace Genode { struct Cpu_state; }
|
||||||
struct Genode::Cpu_state
|
struct Genode::Cpu_state
|
||||||
{
|
{
|
||||||
enum Cpu_exception {
|
enum Cpu_exception {
|
||||||
|
NO_MATH_COPROC = 0x07,
|
||||||
PAGE_FAULT = 0x0e,
|
PAGE_FAULT = 0x0e,
|
||||||
SUPERVISOR_CALL = 0x80,
|
SUPERVISOR_CALL = 0x80,
|
||||||
INTERRUPTS_START = 0x20,
|
INTERRUPTS_START = 0x20,
|
||||||
|
|
Loading…
Reference in New Issue