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
|
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 = \
|
headers = \
|
||||||
$(interface-headers) \
|
$(interface-headers) \
|
||||||
hurd.h \
|
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_save sc_fpsave;
|
||||||
struct i386_fp_regs sc_fpregs;
|
struct i386_fp_regs sc_fpregs;
|
||||||
int sc_fpexcsr; /* FPSR including exception bits. */
|
int sc_fpexcsr; /* FPSR including exception bits. */
|
||||||
|
|
||||||
|
struct i386_xfloat_state *xstate;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Traditional BSD names for some members. */
|
/* Traditional BSD names for some members. */
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cpuid.h>
|
||||||
|
|
||||||
/* This is run on the thread stack after restoring it, to be able to
|
/* This is run on the thread stack after restoring it, to be able to
|
||||||
unlock SS off sigstack. */
|
unlock SS off sigstack. */
|
||||||
static void
|
static void
|
||||||
|
|
@ -123,6 +125,28 @@ __sigreturn (struct sigcontext *scp)
|
||||||
if (scp->sc_onstack)
|
if (scp->sc_onstack)
|
||||||
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
||||||
|
|
||||||
|
#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)
|
if (scp->sc_fpused)
|
||||||
/* Restore the FPU state. Mach conveniently stores the state
|
/* Restore the FPU state. Mach conveniently stores the state
|
||||||
in the format the i387 `frstor' instruction uses to restore it. */
|
in the format the i387 `frstor' instruction uses to restore it. */
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,11 @@
|
||||||
#include "hurdfault.h"
|
#include "hurdfault.h"
|
||||||
#include <intr-msg.h>
|
#include <intr-msg.h>
|
||||||
#include <sys/ucontext.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. */
|
/* Fill in a siginfo_t structure for SA_SIGINFO-enabled handlers. */
|
||||||
static void fill_siginfo (siginfo_t *si, int signo,
|
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 firewall (void);
|
||||||
void *sigsp;
|
void *sigsp;
|
||||||
struct sigcontext *scp;
|
struct sigcontext *scp;
|
||||||
|
vm_size_t xstate_size;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
|
|
@ -145,6 +150,14 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
||||||
struct hurd_userlink link;
|
struct hurd_userlink link;
|
||||||
ucontext_t ucontext;
|
ucontext_t ucontext;
|
||||||
siginfo_t siginfo;
|
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;
|
} *stackframe;
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#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))
|
if (! machine_get_basic_state (ss->thread, state))
|
||||||
return NULL;
|
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.
|
/* Save the original SP in the gratuitous `esp' slot.
|
||||||
We may need to reset the SP (the `uesp' slot) to avoid clobbering an
|
We may need to reset the SP (the `uesp' slot) to avoid clobbering an
|
||||||
interrupted RPC frame. */
|
interrupted RPC frame. */
|
||||||
|
|
@ -196,14 +220,21 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push the arguments to call `trampoline' on the stack. */
|
/* Push the arguments to call `trampoline' on the stack.
|
||||||
sigsp -= sizeof (*stackframe);
|
* The extended state might have a variable size depending on the platform,
|
||||||
#ifdef __x86_64__
|
* so we dynamically allocate it on the stack frame.*/
|
||||||
/* Align SP at 16 bytes. Coupled with the fact that sigreturn_addr is
|
sigsp -= sizeof (*stackframe) + xstate_size;
|
||||||
16-byte aligned within the stackframe struct, this ensures that it ends
|
|
||||||
up on a 16-byte aligned address, as required by the ABI. */
|
/* Align SP at 64 bytes. This is needed for two reasons:
|
||||||
sigsp = (void *) ((uintptr_t) sigsp & ~15UL);
|
* - sigreturn_addr is 16-byte aligned within the stackframe
|
||||||
#endif
|
* 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;
|
stackframe = sigsp;
|
||||||
|
|
||||||
if (_hurdsig_catch_memory_fault (stackframe))
|
if (_hurdsig_catch_memory_fault (stackframe))
|
||||||
|
|
@ -248,6 +279,31 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
||||||
memcpy (&scp->sc_i386_thread_state,
|
memcpy (&scp->sc_i386_thread_state,
|
||||||
&state->basic, sizeof (state->basic));
|
&state->basic, sizeof (state->basic));
|
||||||
|
|
||||||
|
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
|
/* struct sigcontext is laid out so that starting at sc_fpkind mimics
|
||||||
a struct i386_float_state. */
|
a struct i386_float_state. */
|
||||||
_Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
|
_Static_assert (offsetof (struct sigcontext, sc_i386_float_state)
|
||||||
|
|
@ -256,6 +312,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action
|
||||||
ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
|
ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
|
||||||
&state->fpu, &scp->sc_i386_float_state,
|
&state->fpu, &scp->sc_i386_float_state,
|
||||||
sizeof (state->fpu));
|
sizeof (state->fpu));
|
||||||
|
}
|
||||||
|
|
||||||
/* Set up the arguments for the signal handler. */
|
/* Set up the arguments for the signal handler. */
|
||||||
stackframe->signo = signo;
|
stackframe->signo = signo;
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ struct sigcontext
|
||||||
struct i386_fp_save sc_fpsave;
|
struct i386_fp_save sc_fpsave;
|
||||||
struct i386_fp_regs sc_fpregs;
|
struct i386_fp_regs sc_fpregs;
|
||||||
int sc_fpexcsr; /* FPSR including exception bits. */
|
int sc_fpexcsr; /* FPSR including exception bits. */
|
||||||
|
|
||||||
|
struct i386_xfloat_state *xstate;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Traditional BSD names for some members. */
|
/* Traditional BSD names for some members. */
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
#include <hurd/msg.h>
|
#include <hurd/msg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <cpuid.h>
|
||||||
|
|
||||||
/* This is run on the thread stack after restoring it, to be able to
|
/* This is run on the thread stack after restoring it, to be able to
|
||||||
unlock SS off sigstack. */
|
unlock SS off sigstack. */
|
||||||
void
|
void
|
||||||
|
|
@ -116,6 +118,28 @@ __sigreturn (struct sigcontext *scp)
|
||||||
if (scp->sc_onstack)
|
if (scp->sc_onstack)
|
||||||
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
|
||||||
|
|
||||||
|
#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)
|
if (scp->sc_fpused)
|
||||||
/* Restore the FPU state. Mach conveniently stores the state
|
/* Restore the FPU state. Mach conveniently stores the state
|
||||||
in the format the i387 `frstor' instruction uses to restore it. */
|
in the format the i387 `frstor' instruction uses to restore it. */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue