mirror of git://sourceware.org/git/glibc.git
hurd: save xstate during signal handling
* hurd/Makefile: add new tests * hurd/test-sig-rpc-interrupted.c: check xstate save and restore in the case where a signal is delivered to a thread which is waiting for an rpc. This test implements the rpc interruption protocol used by the hurd servers. It was so far passing on Debian thanks to the local-intr-msg-clobber.diff patch, which is now obsolete. * hurd/test-sig-xstate.c: check xstate save and restore in the case where a signal is delivered to a running thread, making sure that the xstate is modified in the signal handler. * hurd/test-xstate.h: add helpers to test xstate * sysdeps/mach/hurd/i386/bits/sigcontext.h: add xstate to the sigcontext structure. + sysdeps/mach/hurd/i386/sigreturn.c: restore xstate from the saved context * sysdeps/mach/hurd/x86/trampoline.c: save xstate if supported. Otherwise we fall back to the previous behaviour of ignoring xstate. * sysdeps/mach/hurd/x86_64/bits/sigcontext.h: add xstate to the sigcontext structure. * sysdeps/mach/hurd/x86_64/sigreturn.c: restore xstate from the saved context Signed-off-by: Luca Dariz <luca@orpolo.org> Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> Message-ID: <20250319171118.142163-1-luca@orpolo.org>
This commit is contained in:
parent
e150ee8709
commit
6d6a6e2dd2
|
|
@ -19,6 +19,11 @@ subdir := hurd
|
|||
|
||||
include ../Makeconfig
|
||||
|
||||
tests := test-sig-xstate \
|
||||
test-sig-rpc-interrupted
|
||||
$(objpfx)test-sig-xstate: $(shared-thread-library)
|
||||
$(objpfx)test-sig-rpc-interrupted: $(shared-thread-library) $(objdir)/hurd/libhurduser.so
|
||||
|
||||
headers = \
|
||||
$(interface-headers) \
|
||||
hurd.h \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
/* Test the state save/restore procedures during signal handling when an
|
||||
interruptible RPC is restarted.
|
||||
|
||||
Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mach/message.h>
|
||||
#include <mach/gnumach.h>
|
||||
#include <mach/mach_traps.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach-shortcuts.h>
|
||||
#include <mach_init.h>
|
||||
#include <hurd/io.h>
|
||||
#include <hurd/io_reply.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
#include "test-xstate.h"
|
||||
|
||||
void handler (int signum, siginfo_t *info, void *context)
|
||||
{
|
||||
printf ("signal %d setting a different CPU state\n", signum);
|
||||
char buf3[XSTATE_BUFFER_SIZE];
|
||||
memset (buf3, 0x77, XSTATE_BUFFER_SIZE);
|
||||
SET_XSTATE (buf3);
|
||||
}
|
||||
|
||||
static const mach_msg_type_t RetCodeCheck = {
|
||||
.msgt_name = (unsigned char) MACH_MSG_TYPE_INTEGER_32,
|
||||
.msgt_size = 32,
|
||||
.msgt_number = 1,
|
||||
.msgt_inline = TRUE,
|
||||
.msgt_longform = FALSE,
|
||||
.msgt_deallocate = FALSE,
|
||||
.msgt_unused = 0
|
||||
};
|
||||
|
||||
|
||||
/* Helper thread to simulate a proper RPC interruption during dignal handling */
|
||||
void* fake_interruptor (void *arg)
|
||||
{
|
||||
int err;
|
||||
sigset_t ss;
|
||||
TEST_COMPARE (sigemptyset (&ss), 0);
|
||||
TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0);
|
||||
TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0);
|
||||
|
||||
struct {
|
||||
mach_msg_header_t Head;
|
||||
} request;
|
||||
mach_port_t rxport = *((mach_port_t*)arg);
|
||||
err = mach_msg (&request.Head, MACH_RCV_MSG, 0, sizeof (request), rxport,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
TEST_COMPARE (request.Head.msgh_bits, 0x1112);
|
||||
TEST_COMPARE (request.Head.msgh_size, sizeof (request.Head));
|
||||
TEST_COMPARE (request.Head.msgh_id, 33000);
|
||||
|
||||
mig_reply_header_t reply;
|
||||
reply.Head = request.Head;
|
||||
reply.Head.msgh_id += 100;
|
||||
reply.RetCodeType = RetCodeCheck;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Helper thread to send a signal to the main thread in the middle of
|
||||
* an interruptible rpc */
|
||||
void* signal_sender (void *arg)
|
||||
{
|
||||
int err;
|
||||
sigset_t ss;
|
||||
TEST_COMPARE (sigemptyset (&ss), 0);
|
||||
TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0);
|
||||
TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0);
|
||||
|
||||
/* Receive the first request, we won't answer to this. */
|
||||
struct {
|
||||
mach_msg_header_t head;
|
||||
char data[64];
|
||||
} m1, m2;
|
||||
mach_port_t rxport = *((mach_port_t*)arg);
|
||||
memset (&m1, 0, sizeof (m1));
|
||||
memset (&m2, 0, sizeof (m2));
|
||||
err = mach_msg (&m1.head, MACH_RCV_MSG, 0, sizeof (m1), rxport,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
/* interrupt the ongoing rpc with a signal, using the
|
||||
* interruptible rpc protocol */
|
||||
pthread_t thintr = xpthread_create (NULL, fake_interruptor, arg);
|
||||
TEST_COMPARE (kill (getpid (), SIGUSR1), 0);
|
||||
xpthread_join (thintr);
|
||||
|
||||
/* Complete the interruption by sending EINTR */
|
||||
mig_reply_header_t reply;
|
||||
reply.Head = m1.head;
|
||||
reply.Head.msgh_id += 100;
|
||||
reply.RetCodeType = RetCodeCheck;
|
||||
reply.RetCode = EINTR;
|
||||
err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
/* Receive the retried rpc, and check that it has the same payload
|
||||
* as the first one. Port names might still be different. */
|
||||
err = mach_msg (&m2.head, MACH_RCV_MSG, 0, sizeof (m2), rxport,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
TEST_COMPARE (m1.head.msgh_bits, m2.head.msgh_bits);
|
||||
TEST_COMPARE (m1.head.msgh_size, m2.head.msgh_size);
|
||||
TEST_COMPARE (m1.head.msgh_id, m2.head.msgh_id);
|
||||
TEST_COMPARE_BLOB (m1.data, sizeof (m1.data), m2.data, sizeof (m2.data));
|
||||
|
||||
/* And finally make the rpc succeed by sending a valid reply */
|
||||
err = io_read_reply (m2.head.msgh_remote_port, MACH_MSG_TYPE_MOVE_SEND_ONCE,
|
||||
KERN_SUCCESS, NULL, 0);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int do_test (void)
|
||||
{
|
||||
#if ! XSTATE_HELPERS_SUPPORTED
|
||||
FAIL_UNSUPPORTED ("Test not supported on this arch.");
|
||||
#endif
|
||||
|
||||
/* Setup signal handling; we need to handle the signal in the main
|
||||
* thread, the other ones will explicitely block SIGUSR1. */
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_flags = SA_RESTART;
|
||||
act.sa_sigaction = &handler;
|
||||
TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0);
|
||||
|
||||
mach_port_t fakeio;
|
||||
int err;
|
||||
err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fakeio);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
err = mach_port_insert_right (mach_task_self (), fakeio, fakeio,
|
||||
MACH_MSG_TYPE_MAKE_SEND);
|
||||
TEST_COMPARE (err, MACH_MSG_SUCCESS);
|
||||
|
||||
pthread_t thsender = xpthread_create (NULL, signal_sender, &fakeio);
|
||||
|
||||
char *buf;
|
||||
mach_msg_type_number_t n;
|
||||
TEST_COMPARE (io_read (fakeio, &buf, &n, 1, 2), 0);
|
||||
|
||||
xpthread_join (thsender);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/* Test the state save/restore procedures during signal handling.
|
||||
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mach/message.h>
|
||||
#include <mach/gnumach.h>
|
||||
#include <mach/mach_traps.h>
|
||||
#include <mach-shortcuts.h>
|
||||
#include <mach_init.h>
|
||||
#include <hurd/io.h>
|
||||
#include <hurd/io_reply.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
#include "test-xstate.h"
|
||||
|
||||
static volatile bool loopflag = true;
|
||||
|
||||
void handler (int signum, siginfo_t *info, void *context)
|
||||
{
|
||||
char buf3[XSTATE_BUFFER_SIZE];
|
||||
memset (buf3, 0x77, XSTATE_BUFFER_SIZE);
|
||||
SET_XSTATE (buf3);
|
||||
printf ("signal %d setting a different CPU state\n", signum);
|
||||
loopflag = false;
|
||||
}
|
||||
|
||||
/* Helper thread to send a signal to the main thread */
|
||||
void* signal_sender (void *arg)
|
||||
{
|
||||
sigset_t ss;
|
||||
assert (! sigemptyset (&ss));
|
||||
assert (! sigaddset (&ss, SIGUSR1));
|
||||
assert (! sigprocmask (SIG_BLOCK, &ss, NULL));
|
||||
|
||||
TEST_COMPARE (kill (getpid (), SIGUSR1), 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int do_test (void)
|
||||
{
|
||||
#if ! XSTATE_HELPERS_SUPPORTED
|
||||
FAIL_UNSUPPORTED ("Test not supported on this arch.");
|
||||
#endif
|
||||
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_sigaction = &handler;
|
||||
TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0);
|
||||
|
||||
pthread_t thsender = xpthread_create (NULL, signal_sender, NULL);
|
||||
|
||||
char buf1[XSTATE_BUFFER_SIZE], buf2[XSTATE_BUFFER_SIZE];
|
||||
memset (buf1, 0x33, XSTATE_BUFFER_SIZE);
|
||||
|
||||
SET_XSTATE (buf1);
|
||||
|
||||
while (loopflag)
|
||||
;
|
||||
|
||||
GET_XSTATE (buf2);
|
||||
TEST_COMPARE_BLOB (buf1, sizeof (buf1), buf2, sizeof (buf2));
|
||||
|
||||
xpthread_join (thsender);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* Helpers to test XSTATE during signal handling
|
||||
|
||||
Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _TEST_XSTATE_H
|
||||
#define _TEST_XSTATE_H
|
||||
|
||||
#if defined __x86_64__ || defined __i386__
|
||||
#define XSTATE_HELPERS_SUPPORTED 1
|
||||
#define XSTATE_BUFFER_SIZE 16
|
||||
#define SET_XSTATE(b) do { \
|
||||
asm volatile ("movups (%0),%%xmm0" :: "r" (b)); \
|
||||
} while (0)
|
||||
|
||||
#define GET_XSTATE(b) do { \
|
||||
asm volatile ("movups %%xmm0,(%0)" :: "r" (b)); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define XSTATE_HELPERS_SUPPORTED 0
|
||||
#define XSTATE_BUFFER_SIZE 1
|
||||
#define SET_XSTATE(b)
|
||||
#endif
|
||||
|
||||
#endif /* _TEST_XSTATE_H */
|
||||
|
|
@ -88,6 +88,8 @@ struct sigcontext
|
|||
struct i386_fp_save sc_fpsave;
|
||||
struct i386_fp_regs sc_fpregs;
|
||||
int sc_fpexcsr; /* FPSR including exception bits. */
|
||||
|
||||
struct i386_xfloat_state *xstate;
|
||||
};
|
||||
|
||||
/* Traditional BSD names for some members. */
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cpuid.h>
|
||||
|
||||
/* This is run on the thread stack after restoring it, to be able to
|
||||
unlock SS off sigstack. */
|
||||
static void
|
||||
|
|
@ -123,10 +125,32 @@ __sigreturn (struct sigcontext *scp)
|
|||
if (scp->sc_onstack)
|
||||
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
||||
|
||||
if (scp->sc_fpused)
|
||||
/* Restore the FPU state. Mach conveniently stores the state
|
||||
in the format the i387 `frstor' instruction uses to restore it. */
|
||||
asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
|
||||
#ifdef i386_XFLOAT_STATE
|
||||
if ((scp->xstate) && (scp->xstate->initialized))
|
||||
{
|
||||
unsigned eax, ebx, ecx, edx;
|
||||
__cpuid_count(0xd, 0, eax, ebx, ecx, edx);
|
||||
switch (scp->xstate->fp_save_kind)
|
||||
{
|
||||
case 0: // FNSAVE
|
||||
asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
|
||||
break;
|
||||
case 1: // FXSAVE
|
||||
asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
|
||||
"a" (eax), "d" (edx));
|
||||
break;
|
||||
default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
|
||||
asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
|
||||
"a" (eax), "d" (edx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (scp->sc_fpused)
|
||||
/* Restore the FPU state. Mach conveniently stores the state
|
||||
in the format the i387 `frstor' instruction uses to restore it. */
|
||||
asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
|
||||
|
||||
{
|
||||
/* There are convenient instructions to pop state off the stack, so we
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@
|
|||
#include "hurdfault.h"
|
||||
#include <intr-msg.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include <mach/x86_64/mach_i386.h>
|
||||
#else
|
||||
#include <mach/i386/mach_i386.h>
|
||||
#endif
|
||||
|
||||
/* Fill in a siginfo_t structure for SA_SIGINFO-enabled handlers. */
|
||||
static void fill_siginfo (siginfo_t *si, int signo,
|
||||
|
|
@ -106,6 +110,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
|||
void firewall (void);
|
||||
void *sigsp;
|
||||
struct sigcontext *scp;
|
||||
vm_size_t xstate_size;
|
||||
struct
|
||||
{
|
||||
union
|
||||
|
|
@ -145,6 +150,14 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
|||
struct hurd_userlink link;
|
||||
ucontext_t ucontext;
|
||||
siginfo_t siginfo;
|
||||
#ifdef __x86_64__
|
||||
char _pad2[56];
|
||||
#else
|
||||
char _pad2[20];
|
||||
#endif
|
||||
char xstate[];
|
||||
/* Don't add anything after xstate, as it's dynamically
|
||||
sized. */
|
||||
} *stackframe;
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
|
@ -170,6 +183,17 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
|||
if (! machine_get_basic_state (ss->thread, state))
|
||||
return NULL;
|
||||
|
||||
/* Initialize the size of the CPU extended state, to be saved during
|
||||
* signal handling */
|
||||
#ifdef i386_XFLOAT_STATE
|
||||
_Static_assert ((sizeof(*stackframe) + sizeof(struct i386_xfloat_state)) % 64 == 0,
|
||||
"stackframe size must be multiple of 64-byte minus "
|
||||
"sizeof(struct i386_xfloat_state), please adjust _pad2");
|
||||
|
||||
if (__i386_get_xstate_size(__mach_host_self(), &xstate_size))
|
||||
#endif
|
||||
xstate_size = 0;
|
||||
|
||||
/* Save the original SP in the gratuitous `esp' slot.
|
||||
We may need to reset the SP (the `uesp' slot) to avoid clobbering an
|
||||
interrupted RPC frame. */
|
||||
|
|
@ -196,14 +220,21 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Push the arguments to call `trampoline' on the stack. */
|
||||
sigsp -= sizeof (*stackframe);
|
||||
#ifdef __x86_64__
|
||||
/* Align SP at 16 bytes. Coupled with the fact that sigreturn_addr is
|
||||
16-byte aligned within the stackframe struct, this ensures that it ends
|
||||
up on a 16-byte aligned address, as required by the ABI. */
|
||||
sigsp = (void *) ((uintptr_t) sigsp & ~15UL);
|
||||
#endif
|
||||
/* Push the arguments to call `trampoline' on the stack.
|
||||
* The extended state might have a variable size depending on the platform,
|
||||
* so we dynamically allocate it on the stack frame.*/
|
||||
sigsp -= sizeof (*stackframe) + xstate_size;
|
||||
|
||||
/* Align SP at 64 bytes. This is needed for two reasons:
|
||||
* - sigreturn_addr is 16-byte aligned within the stackframe
|
||||
* struct, and this ensures that it ends up on a 16-byte aligned
|
||||
* address, as required by the ABI.
|
||||
* - the XSAVE state needs to be aligned at 64 bytes (on both i386 and
|
||||
* x86_64), so we align the stackframe also at 64 bytes and add the
|
||||
* required padding at the end, see the _pad2 field.
|
||||
*/
|
||||
sigsp = (void *) ((uintptr_t) sigsp & ~63UL);
|
||||
|
||||
stackframe = sigsp;
|
||||
|
||||
if (_hurdsig_catch_memory_fault (stackframe))
|
||||
|
|
@ -248,14 +279,40 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
|||
memcpy (&scp->sc_i386_thread_state,
|
||||
&state->basic, sizeof (state->basic));
|
||||
|
||||
/* struct sigcontext is laid out so that starting at sc_fpkind mimics
|
||||
a struct i386_float_state. */
|
||||
_Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
|
||||
% __alignof__ (struct i386_float_state) == 0,
|
||||
"sc_i386_float_state layout mismatch");
|
||||
ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
|
||||
&state->fpu, &scp->sc_i386_float_state,
|
||||
sizeof (state->fpu));
|
||||
scp->xstate = NULL;
|
||||
#ifdef i386_XFLOAT_STATE
|
||||
if (xstate_size > 0)
|
||||
{
|
||||
mach_msg_type_number_t got = (xstate_size / sizeof (int));
|
||||
|
||||
ok = (! __thread_get_state (ss->thread, i386_XFLOAT_STATE,
|
||||
(thread_state_t) stackframe->xstate, &got)
|
||||
&& got == (xstate_size / sizeof (int)));
|
||||
|
||||
if (((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5)
|
||||
/* We support up to XSAVES */
|
||||
ok = 0;
|
||||
|
||||
if (ok)
|
||||
{
|
||||
scp->xstate = (struct i386_xfloat_state*) stackframe->xstate;
|
||||
assert((uintptr_t)scp->xstate->hw_state % 64 == 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
ok = 0;
|
||||
if (!ok)
|
||||
{
|
||||
/* struct sigcontext is laid out so that starting at sc_fpkind mimics
|
||||
a struct i386_float_state. */
|
||||
_Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
|
||||
% __alignof__ (struct i386_float_state) == 0,
|
||||
"sc_i386_float_state layout mismatch");
|
||||
ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
|
||||
&state->fpu, &scp->sc_i386_float_state,
|
||||
sizeof (state->fpu));
|
||||
}
|
||||
|
||||
/* Set up the arguments for the signal handler. */
|
||||
stackframe->signo = signo;
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ struct sigcontext
|
|||
struct i386_fp_save sc_fpsave;
|
||||
struct i386_fp_regs sc_fpregs;
|
||||
int sc_fpexcsr; /* FPSR including exception bits. */
|
||||
|
||||
struct i386_xfloat_state *xstate;
|
||||
};
|
||||
|
||||
/* Traditional BSD names for some members. */
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
#include <hurd/msg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cpuid.h>
|
||||
|
||||
/* This is run on the thread stack after restoring it, to be able to
|
||||
unlock SS off sigstack. */
|
||||
void
|
||||
|
|
@ -116,10 +118,32 @@ __sigreturn (struct sigcontext *scp)
|
|||
if (scp->sc_onstack)
|
||||
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
||||
|
||||
if (scp->sc_fpused)
|
||||
/* Restore the FPU state. Mach conveniently stores the state
|
||||
in the format the i387 `frstor' instruction uses to restore it. */
|
||||
asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
|
||||
#ifdef i386_XFLOAT_STATE
|
||||
if ((scp->xstate) && (scp->xstate->initialized))
|
||||
{
|
||||
unsigned eax, ebx, ecx, edx;
|
||||
__cpuid_count(0xd, 0, eax, ebx, ecx, edx);
|
||||
switch (scp->xstate->fp_save_kind)
|
||||
{
|
||||
case 0: // FNSAVE
|
||||
asm volatile("frstor %0" : : "m" (scp->xstate->hw_state));
|
||||
break;
|
||||
case 1: // FXSAVE
|
||||
asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \
|
||||
"a" (eax), "d" (edx));
|
||||
break;
|
||||
default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES
|
||||
asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \
|
||||
"a" (eax), "d" (edx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (scp->sc_fpused)
|
||||
/* Restore the FPU state. Mach conveniently stores the state
|
||||
in the format the i387 `frstor' instruction uses to restore it. */
|
||||
asm volatile ("frstor %0" : : "m" (scp->sc_fpsave));
|
||||
|
||||
/* Copy the registers onto the user's stack, to be able to release the
|
||||
altstack (by unlocking sigstate). Note that unless an altstack is used,
|
||||
|
|
|
|||
Loading…
Reference in New Issue