Save and restore FPU state across suspend and resume. In earlier revisions

of this patch, resumectx() called npxresume() directly, but that doesn't
work because resumectx() runs with a non-standard %cs selector.  Instead,
all of the FPU suspend/resume handling is done in C.

MFC after:	1 week
This commit is contained in:
John Baldwin 2014-08-30 17:48:38 +00:00
parent 53c7f22849
commit 0f68c1833b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=270850
6 changed files with 57 additions and 43 deletions

View file

@ -1522,9 +1522,15 @@ cpususpend_handler(void)
cpu = PCPU_GET(cpuid);
if (savectx(susppcbs[cpu])) {
#ifdef DEV_NPX
npxsuspend(&suspcbs[cpu]->pcb_fpususpend);
#endif
wbinvd();
CPU_SET_ATOMIC(cpu, &suspended_cpus);
} else {
#ifdef DEV_NPX
npxresume(&suspcbs[cpu]->pcb_fpususpend);
#endif
pmap_init_pat();
PCPU_SET(switchtime, 0);
PCPU_SET(switchticks, ticks);

View file

@ -416,45 +416,6 @@ ENTRY(savectx)
sldt PCB_LDT(%ecx)
str PCB_TR(%ecx)
#ifdef DEV_NPX
/*
* If fpcurthread == NULL, then the npx h/w state is irrelevant and the
* state had better already be in the pcb. This is true for forks
* but not for dumps (the old book-keeping with FP flags in the pcb
* always lost for dumps because the dump pcb has 0 flags).
*
* If fpcurthread != NULL, then we have to save the npx h/w state to
* fpcurthread's pcb and copy it to the requested pcb, or save to the
* requested pcb and reload. Copying is easier because we would
* have to handle h/w bugs for reloading. We used to lose the
* parent's npx state for forks by forgetting to reload.
*/
pushfl
CLI
movl PCPU(FPCURTHREAD),%eax
testl %eax,%eax
je 1f
pushl %ecx
movl TD_PCB(%eax),%eax
movl PCB_SAVEFPU(%eax),%eax
pushl %eax
pushl %eax
call npxsave
addl $4,%esp
popl %eax
popl %ecx
pushl $PCB_SAVEFPU_SIZE
leal PCB_USERFPU(%ecx),%ecx
pushl %ecx
pushl %eax
call bcopy
addl $12,%esp
1:
popfl
#endif /* DEV_NPX */
movl $1,%eax
ret
END(savectx)
@ -519,10 +480,6 @@ ENTRY(resumectx)
movl PCB_DR7(%ecx),%eax
movl %eax,%dr7
#ifdef DEV_NPX
/* XXX FIX ME */
#endif
/* Restore other registers */
movl PCB_EDI(%ecx),%edi
movl PCB_ESI(%ecx),%esi

View file

@ -53,8 +53,10 @@ void npxexit(struct thread *td);
int npxformat(void);
int npxgetregs(struct thread *td);
void npxinit(void);
void npxresume(union savefpu *addr);
void npxsave(union savefpu *addr);
void npxsetregs(struct thread *td, union savefpu *addr);
void npxsuspend(union savefpu *addr);
int npxtrap_x87(void);
int npxtrap_sse(void);
void npxuserinited(struct thread *);

View file

@ -90,6 +90,8 @@ struct pcb {
struct region_descriptor pcb_idt;
uint16_t pcb_ldt;
uint16_t pcb_tr;
union savefpu pcb_fpususpend;
};
#ifdef _KERNEL

View file

@ -761,6 +761,43 @@ npxsave(addr)
PCPU_SET(fpcurthread, NULL);
}
/*
* Unconditionally save the current co-processor state across suspend and
* resume.
*/
void
npxsuspend(union savefpu *addr)
{
register_t cr0;
if (!hw_float)
return;
if (PCPU_GET(fpcurthread) == NULL) {
*addr = npx_initialstate;
return;
}
cr0 = rcr0();
clts();
fpusave(addr);
load_cr0(cr0);
}
void
npxresume(union savefpu *addr)
{
register_t cr0;
if (!hw_float)
return;
cr0 = rcr0();
clts();
npxinit();
stop_emulating();
fpurstor(addr);
load_cr0(cr0);
}
void
npxdrop()
{

View file

@ -30,6 +30,10 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifdef __i386__
#include "opt_npx.h"
#endif
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/eventhandler.h>
@ -203,6 +207,8 @@ acpi_sleep_machdep(struct acpi_softc *sc, int state)
if (savectx(susppcbs[0])) {
#ifdef __amd64__
fpususpend(susppcbs[0]->pcb_fpususpend);
#elif defined(DEV_NPX)
npxsuspend(&susppcbs[0]->pcb_fpususpend);
#endif
#ifdef SMP
if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) {
@ -237,6 +243,10 @@ acpi_sleep_machdep(struct acpi_softc *sc, int state)
for (;;)
ia32_pause();
} else {
#ifdef DEV_NPX
npxresume(&susppcbs[0]->pcb_fpususpend);
#endif
}
return (1); /* wakeup successfully */