[PATCH 19/22] ntdll: Add PPC64 signal handling

André Hentschel nerv at dawncrow.de
Sun Aug 16 14:01:57 CDT 2020


Signed-off-by: André Hentschel <nerv at dawncrow.de>
---
 dlls/ntdll/Makefile.in             |    2 +
 dlls/ntdll/loader.c                |    4 +-
 dlls/ntdll/ntdll.spec              |    2 +-
 dlls/ntdll/ntdll_misc.h            |    2 +
 dlls/ntdll/signal_powerpc64.c      |  279 ++++++
 dlls/ntdll/unix/loader.c           |    2 +-
 dlls/ntdll/unix/signal_powerpc64.c | 1327 ++++++++++++++++++++++++++++
 dlls/ntdll/unix/system.c           |    7 +
 dlls/ntdll/unix/unix_private.h     |    6 +
 dlls/ntdll/unix/virtual.c          |    2 +
 10 files changed, 1630 insertions(+), 3 deletions(-)
 create mode 100644 dlls/ntdll/signal_powerpc64.c
 create mode 100644 dlls/ntdll/unix/signal_powerpc64.c

diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
index bc8215b6bfb..8df18b5f64a 100644
--- a/dlls/ntdll/Makefile.in
+++ b/dlls/ntdll/Makefile.in
@@ -38,6 +38,7 @@ C_SRCS = \
 	signal_arm.c \
 	signal_arm64.c \
 	signal_i386.c \
+	signal_powerpc64.c \
 	signal_x86_64.c \
 	string.c \
 	sync.c \
@@ -57,6 +58,7 @@ C_SRCS = \
 	unix/signal_arm.c \
 	unix/signal_arm64.c \
 	unix/signal_i386.c \
+	unix/signal_powerpc64.c \
 	unix/signal_x86_64.c \
 	unix/sync.c \
 	unix/system.c \
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 6290cbcb4e6..c5e7f1935ab 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -2032,8 +2032,10 @@ static BOOL is_valid_binary( HMODULE module, const pe_image_info_t *info )
 #elif defined(_WIN64)  /* support 32-bit IL-only images on 64-bit */
 #ifdef __x86_64__
     if (info->machine == IMAGE_FILE_MACHINE_AMD64) return TRUE;
-#else
+#elif defined(__aarch64__)
     if (info->machine == IMAGE_FILE_MACHINE_ARM64) return TRUE;
+#elif defined(__powerpc64__)
+    if (info->machine == IMAGE_FILE_MACHINE_POWERPC64) return TRUE;
 #endif
     if (!info->contains_code) return TRUE;
     if (!(info->image_flags & IMAGE_FLAGS_ComPlusNativeReady))
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 2a95dddf202..9707163fbeb 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -188,7 +188,7 @@
 @ stub NtCreateToken
 @ stdcall -syscall NtCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr)
 # @ stub NtCreateWaitablePort
-@ stdcall -arch=win32,arm64 NtCurrentTeb()
+@ stdcall -arch=win32,arm64,powerpc64 NtCurrentTeb()
 # @ stub NtDebugActiveProcess
 # @ stub NtDebugContinue
 @ stdcall -syscall NtDelayExecution(long ptr)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index a97dadb2a5f..4836a382462 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -40,6 +40,8 @@
 
 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
 static const UINT_PTR page_size = 0x1000;
+#elif defined(__powerpc64__)
+static const UINT_PTR page_size = 0x10000;
 #else
 extern UINT_PTR page_size DECLSPEC_HIDDEN;
 #endif
diff --git a/dlls/ntdll/signal_powerpc64.c b/dlls/ntdll/signal_powerpc64.c
new file mode 100644
index 00000000000..c5217b5ff37
--- /dev/null
+++ b/dlls/ntdll/signal_powerpc64.c
@@ -0,0 +1,279 @@
+/*
+ * PowerPC64 signal handling routines
+ *
+ * Copyright 2019 Timothy Pearson <tpearson at raptorengineering.com>
+ * Copyright 2020 André Hentschel
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef __powerpc64__
+
+#include <assert.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winternl.h"
+#include "wine/exception.h"
+#include "ntdll_misc.h"
+#include "wine/debug.h"
+#include "winnt.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(seh);
+
+/* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */
+struct MSVCRT_JUMP_BUFFER
+{
+    unsigned __int64 Frame;
+    unsigned __int64 Reserved;
+    unsigned __int64 X19;
+    unsigned __int64 X20;
+    unsigned __int64 X21;
+    unsigned __int64 X22;
+    unsigned __int64 X23;
+    unsigned __int64 X24;
+    unsigned __int64 X25;
+    unsigned __int64 X26;
+    unsigned __int64 X27;
+    unsigned __int64 X28;
+    unsigned __int64 Fp;
+    unsigned __int64 Lr;
+    unsigned __int64 Sp;
+    unsigned long Fpcr;
+    unsigned long Fpsr;
+    double D[8];
+};
+
+
+/*******************************************************************
+ *         is_valid_frame
+ */
+static inline BOOL is_valid_frame( ULONG_PTR frame )
+{
+    if (frame & 7) return FALSE;
+    return ((void *)frame >= NtCurrentTeb()->Tib.StackLimit &&
+            (void **)frame < (void **)NtCurrentTeb()->Tib.StackBase - 1);
+}
+
+
+/***********************************************************************
+ *		RtlCaptureContext (NTDLL.@)
+ */
+void WINAPI RtlCaptureContext( CONTEXT *context )
+{
+    FIXME("not implemented\n");
+    memset( context, 0, sizeof(*context) );
+}
+
+
+/**********************************************************************
+ *           call_stack_handlers
+ *
+ * Call the stack handlers chain.
+ */
+static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
+{
+    EXCEPTION_POINTERS ptrs;
+
+    FIXME( "not implemented on PowerPC 64\n" );
+
+    /* hack: call unhandled exception filter directly */
+    ptrs.ExceptionRecord = rec;
+    ptrs.ContextRecord = context;
+    call_unhandled_exception_filter( &ptrs );
+    return STATUS_UNHANDLED_EXCEPTION;
+}
+
+
+/*******************************************************************
+ *		KiUserExceptionDispatcher (NTDLL.@)
+ */
+NTSTATUS WINAPI KiUserExceptionDispatcher( EXCEPTION_RECORD *rec, CONTEXT *context )
+{
+    NTSTATUS status;
+    DWORD c;
+
+    ERR( "code=%x flags=%x addr=%p pc=%lx tid=%04x\n",
+           rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
+           context->Iar, GetCurrentThreadId() );
+    for (c = 0; c < rec->NumberParameters; c++)
+        TRACE( " info[%d]=%016lx\n", c, rec->ExceptionInformation[c] );
+
+    if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
+    {
+        if (rec->ExceptionInformation[1] >> 16)
+            MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
+                     rec->ExceptionAddress,
+                     (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
+        else
+            MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n",
+                     rec->ExceptionAddress,
+                     (char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
+    }
+    else
+    {
+        /*TRACE("  x0=%016lx  x1=%016lx  x2=%016lx  x3=%016lx\n",
+              context->u.s.X0, context->u.s.X1, context->u.s.X2, context->u.s.X3 );
+        TRACE("  x4=%016lx  x5=%016lx  x6=%016lx  x7=%016lx\n",
+              context->u.s.X4, context->u.s.X5, context->u.s.X6, context->u.s.X7 );
+        TRACE("  x8=%016lx  x9=%016lx x10=%016lx x11=%016lx\n",
+              context->u.s.X8, context->u.s.X9, context->u.s.X10, context->u.s.X11 );
+        TRACE(" x12=%016lx x13=%016lx x14=%016lx x15=%016lx\n",
+              context->u.s.X12, context->u.s.X13, context->u.s.X14, context->u.s.X15 );
+        TRACE(" x16=%016lx x17=%016lx x18=%016lx x19=%016lx\n",
+              context->u.s.X16, context->u.s.X17, context->u.s.X18, context->u.s.X19 );
+        TRACE(" x20=%016lx x21=%016lx x22=%016lx x23=%016lx\n",
+              context->u.s.X20, context->u.s.X21, context->u.s.X22, context->u.s.X23 );
+        TRACE(" x24=%016lx x25=%016lx x26=%016lx x27=%016lx\n",
+              context->u.s.X24, context->u.s.X25, context->u.s.X26, context->u.s.X27 );
+        TRACE(" x28=%016lx  fp=%016lx  lr=%016lx  sp=%016lx\n",
+              context->u.s.X28, context->u.s.Fp, context->u.s.Lr, context->Sp );*/
+    }
+
+    if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION)
+        NtContinue( context, FALSE );
+
+    if ((status = call_stack_handlers( rec, context )) != STATUS_UNHANDLED_EXCEPTION)
+        NtContinue( context, FALSE );
+
+    if (status != STATUS_UNHANDLED_EXCEPTION) RtlRaiseStatus( status );
+    return NtRaiseException( rec, context, FALSE );
+}
+
+/***********************************************************************
+ * Definitions for Win32 unwind tables
+ */
+
+struct unwind_info
+{
+    DWORD function_length : 18;
+    DWORD version : 2;
+    DWORD x : 1;
+    DWORD e : 1;
+    DWORD epilog : 5;
+    DWORD codes : 5;
+};
+
+struct unwind_info_ext
+{
+    WORD epilog;
+    BYTE codes;
+    BYTE reserved;
+};
+
+struct unwind_info_epilog
+{
+    DWORD offset : 18;
+    DWORD res : 4;
+    DWORD index : 10;
+};
+
+
+/**********************************************************************
+ *              RtlVirtualUnwind   (NTDLL.@)
+ */
+PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
+                               void *func, CONTEXT *context,
+                               PVOID *handler_data, ULONG_PTR *frame_ret,
+                               void *ctx_ptr )
+{
+    FIXME(" STUB!!!\n");
+    return NULL;
+}
+
+/*******************************************************************
+ *              RtlRestoreContext (NTDLL.@)
+ */
+void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
+{
+    FIXME( "stub!\n" );
+}
+
+/*******************************************************************
+ *		RtlUnwindEx (NTDLL.@)
+ */
+void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec,
+                         PVOID retval, CONTEXT *context, void *table )
+{
+    FIXME( "stub!\n" );
+}
+
+
+/***********************************************************************
+ *            RtlUnwind  (NTDLL.@)
+ */
+void WINAPI RtlUnwind( void *frame, void *target_ip, EXCEPTION_RECORD *rec, void *retval )
+{
+    CONTEXT context;
+    RtlUnwindEx( frame, target_ip, rec, retval, &context, NULL );
+}
+
+
+/***********************************************************************
+ *		RtlRaiseException (NTDLL.@)
+ */
+void WINAPI RtlRaiseException( EXCEPTION_RECORD *rec )
+{
+    CONTEXT context;
+    NTSTATUS status;
+
+    FIXME( "semi stub!\n" );
+
+    RtlCaptureContext( &context );
+    rec->ExceptionAddress = (void *)context.Iar;
+    status = NtRaiseException( rec, &context, TRUE );
+    if (status) RtlRaiseStatus( status );
+}
+
+/*************************************************************************
+ *             RtlCaptureStackBackTrace (NTDLL.@)
+ */
+USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, ULONG *hash )
+{
+    FIXME( "(%d, %d, %p, %p) stub!\n", skip, count, buffer, hash );
+    return 0;
+}
+
+/**********************************************************************
+ *              DbgBreakPoint   (NTDLL.@)
+ */
+void WINAPI DbgBreakPoint(void)
+{
+    FIXME( "stub!\n" );
+}
+
+/**********************************************************************
+ *              DbgUserBreakPoint   (NTDLL.@)
+ */
+void WINAPI DbgUserBreakPoint(void)
+{
+    FIXME( "stub!\n" );
+}
+/**********************************************************************
+ *           NtCurrentTeb   (NTDLL.@)
+ */
+TEB * WINAPI NtCurrentTeb(void)
+{
+    return unix_funcs->NtCurrentTeb();
+}
+
+#endif  /* __aarch64__ */
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index e7cc050ba9d..81607cd0d50 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -474,7 +474,7 @@ static NTSTATUS loader_exec( const char *loader, char **argv, client_cpu_t cpu )
  */
 NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_info )
 {
-    int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64);
+    int is_child_64bit = (pe_info->cpu == CPU_x86_64 || pe_info->cpu == CPU_ARM64 || pe_info->cpu == CPU_POWERPC64);
     ULONGLONG res_start = pe_info->base;
     ULONGLONG res_end = pe_info->base + pe_info->map_size;
     const char *loader = argv0;
diff --git a/dlls/ntdll/unix/signal_powerpc64.c b/dlls/ntdll/unix/signal_powerpc64.c
new file mode 100644
index 00000000000..3e29a1b34c3
--- /dev/null
+++ b/dlls/ntdll/unix/signal_powerpc64.c
@@ -0,0 +1,1327 @@
+/*
+ * PowerPC64 signal handling routines
+ *
+ * Copyright 2019 Timothy Pearson <tpearson at raptorengineering.com>
+ * Copyright 2020 André Hentschel
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#if 0
+#pragma makedep unix
+#endif
+
+#ifdef __powerpc64__
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifdef HAVE_SYSCALL_H
+# include <syscall.h>
+#else
+# ifdef HAVE_SYS_SYSCALL_H
+#  include <sys/syscall.h>
+# endif
+#endif
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifdef HAVE_SYS_UCONTEXT_H
+# include <sys/ucontext.h>
+#endif
+#ifdef HAVE_LIBUNWIND
+# define UNW_LOCAL_ONLY
+# include <libunwind.h>
+#endif
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winnt.h"
+#include "winternl.h"
+#include "wine/exception.h"
+#include "wine/asm.h"
+#include "unix_private.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(seh);
+
+/***********************************************************************
+ * signal context platform-specific definitions
+ */
+#ifdef linux
+
+/* All Registers access - only for local access */
+# define REG_sig(reg_name, context)		((context)->uc_mcontext.regs->reg_name)
+
+
+/* Gpr Registers access  */
+# define GPR_sig(reg_num, context)		REG_sig(gpr[reg_num], context)
+
+# define IAR_sig(context)			REG_sig(nip, context)	/* Program counter */
+# define MSR_sig(context)			REG_sig(msr, context)   /* Machine State Register (Supervisor) */
+# define CTR_sig(context)			REG_sig(ctr, context)   /* Count register */
+
+# define XER_sig(context)			REG_sig(xer, context) /* User's integer exception register */
+# define LR_sig(context)			REG_sig(link, context) /* Link register */
+# define CR_sig(context)			REG_sig(ccr, context) /* Condition register */
+
+/* Float Registers access  */
+# define FLOAT_sig(reg_num, context)		(((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num])
+
+# define FPSCR_sig(context)			(*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4)))
+
+/* Exception Registers access */
+# define DAR_sig(context)			REG_sig(dar, context)
+# define DSISR_sig(context)			REG_sig(dsisr, context)
+# define TRAP_sig(context)			REG_sig(trap, context)
+
+#endif /* linux */
+
+static pthread_key_t teb_key;
+
+typedef void (*raise_func)( EXCEPTION_RECORD *rec, CONTEXT *context );
+
+/* stack layout when calling an exception raise function */
+struct stack_layout
+{
+    CONTEXT           context;
+    EXCEPTION_RECORD  rec;
+    void             *redzone[2];
+};
+
+struct syscall_frame
+{
+    ULONG64 r1;
+    ULONG64 cr; /* unused */
+    ULONG64 pad0;
+    ULONG64 r2;
+    ULONG64 r3, r4, r5, r6, r7, r8, r9, r10;
+    struct syscall_frame *prev_frame;
+    ULONG64 pad1;
+    ULONG64 r14, r15, r16, r17, r18, r19, r20, r21, r22;
+    ULONG64 r23, r24, r25, r26, r27, r28, r29, r30, r31;
+    ULONG64 thunk_r1;
+    ULONG64 pad2;
+    ULONG64 thunk_addr;
+    ULONG64 thunk_r2;
+    ULONG64 ret_addr;
+    ULONG64 r11;
+    ULONG64 r12;
+    ULONG64 pad3;
+};
+
+struct ppc64_thread_data
+{
+    void                 *exit_frame;    /* 02f0 exit frame pointer */
+    struct syscall_frame *syscall_frame; /* 02f8 frame pointer on syscall entry */
+    CONTEXT              *context;       /* 0300 context to set with SIGUSR2 */
+};
+
+C_ASSERT( sizeof(struct ppc64_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) );
+C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct ppc64_thread_data, exit_frame ) == 0x2f0 );
+C_ASSERT( offsetof( TEB, GdiTebBatch ) + offsetof( struct ppc64_thread_data, syscall_frame ) == 0x2f8 );
+
+static inline struct ppc64_thread_data *ppc64_thread_data(void)
+{
+    return (struct ppc64_thread_data *)ntdll_get_thread_data()->cpu_data;
+}
+
+
+/***********************************************************************
+ *           unwind_builtin_dll
+ *
+ * Equivalent of RtlVirtualUnwind for builtin modules.
+ */
+NTSTATUS CDECL unwind_builtin_dll( ULONG type, struct _DISPATCHER_CONTEXT *dispatch, CONTEXT *context )
+{
+    FIXME("not implemented\n");
+    return STATUS_UNSUCCESSFUL;
+}
+
+
+/***********************************************************************
+ *           save_context
+ *
+ * Set the register values from a sigcontext.
+ */
+static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
+{
+#define C(x) context->Gpr##x = GPR_sig(x,sigcontext)
+    /* Save Gpr registers */
+    C(0); C(1); C(2); C(3); C(4); C(5); C(6); C(7); C(8); C(9); C(10);
+    C(11); C(12); C(13); C(14); C(15); C(16); C(17); C(18); C(19); C(20);
+    C(21); C(22); C(23); C(24); C(25); C(26); C(27); C(28); C(29); C(30);
+    C(31);
+#undef C
+
+    context->Iar = IAR_sig(sigcontext);  /* Program Counter */
+    context->Msr = MSR_sig(sigcontext);  /* Machine State Register (Supervisor) */
+    context->Ctr = CTR_sig(sigcontext);
+
+    context->Xer = XER_sig(sigcontext);
+    context->Lr  = LR_sig(sigcontext);
+    context->Cr  = CR_sig(sigcontext);
+
+    /* Saving Exception regs */
+    context->Dar   = DAR_sig(sigcontext);
+    context->Dsisr = DSISR_sig(sigcontext);
+    context->Trap  = TRAP_sig(sigcontext);
+}
+
+
+/***********************************************************************
+ *           restore_context
+ *
+ * Build a sigcontext from the register values.
+ */
+static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
+{
+#define C(x)  GPR_sig(x,sigcontext) = context->Gpr##x
+    C(0); C(1); C(2); C(3); C(4); C(5); C(6); C(7); C(8); C(9); C(10);
+    C(11); C(12); C(13); C(14); C(15); C(16); C(17); C(18); C(19); C(20);
+    C(21); C(22); C(23); C(24); C(25); C(26); C(27); C(28); C(29); C(30);
+    C(31);
+#undef C
+
+    IAR_sig(sigcontext) = context->Iar;  /* Program Counter */
+    MSR_sig(sigcontext) = context->Msr;  /* Machine State Register (Supervisor) */
+    CTR_sig(sigcontext) = context->Ctr;
+
+    XER_sig(sigcontext) = context->Xer;
+    LR_sig(sigcontext) = context->Lr;
+    CR_sig(sigcontext) = context->Cr;
+
+    /* Setting Exception regs */
+    DAR_sig(sigcontext) = context->Dar;
+    DSISR_sig(sigcontext) = context->Dsisr;
+    TRAP_sig(sigcontext) = context->Trap;
+}
+
+
+/***********************************************************************
+ *           save_fpu
+ *
+ * Set the FPU context from a sigcontext.
+ */
+static void save_fpu( CONTEXT *context, ucontext_t *sigcontext )
+{
+#define C(x)   context->Fpr##x = FLOAT_sig(x,sigcontext)
+    C(0); C(1); C(2); C(3); C(4); C(5); C(6); C(7); C(8); C(9); C(10);
+    C(11); C(12); C(13); C(14); C(15); C(16); C(17); C(18); C(19); C(20);
+    C(21); C(22); C(23); C(24); C(25); C(26); C(27); C(28); C(29); C(30);
+    C(31);
+#undef C
+    context->Fpscr = FPSCR_sig(sigcontext);
+}
+
+
+/***********************************************************************
+ *           restore_fpu
+ *
+ * Restore the FPU context to a sigcontext.
+ */
+static void restore_fpu( CONTEXT *context, ucontext_t *sigcontext )
+{
+#define C(x)  FLOAT_sig(x,sigcontext) = context->Fpr##x
+    C(0); C(1); C(2); C(3); C(4); C(5); C(6); C(7); C(8); C(9); C(10);
+    C(11); C(12); C(13); C(14); C(15); C(16); C(17); C(18); C(19); C(20);
+    C(21); C(22); C(23); C(24); C(25); C(26); C(27); C(28); C(29); C(30);
+    C(31);
+#undef C
+    FPSCR_sig(sigcontext) = context->Fpscr;
+}
+
+
+/***********************************************************************
+ *           set_cpu_context
+ *
+ * Set the new CPU context.
+ */
+void DECLSPEC_HIDDEN set_cpu_context( const CONTEXT *context );
+__ASM_GLOBAL_FUNC( set_cpu_context,
+                   "ld 1, 272(3)\n\t"
+                   "ld 2, 280(3)\n\t"
+                   "ld 4, 296(3)\n\t"
+                   "ld 5, 304(3)\n\t"
+                   "ld 6, 312(3)\n\t"
+                   "ld 7, 320(3)\n\t"
+                   "ld 8, 328(3)\n\t"
+                   "ld 9, 336(3)\n\t"
+                   "ld 10, 344(3)\n\t"
+                   "ld 11, 352(3)\n\t"
+                   "ld 14, 376(3)\n\t"
+                   "ld 15, 384(3)\n\t"
+                   "ld 16, 392(3)\n\t"
+                   "ld 17, 400(3)\n\t"
+                   "ld 18, 408(3)\n\t"
+                   "ld 19, 416(3)\n\t"
+                   "ld 20, 424(3)\n\t"
+                   "ld 21, 432(3)\n\t"
+                   "ld 22, 440(3)\n\t"
+                   "ld 23, 448(3)\n\t"
+                   "ld 24, 456(3)\n\t"
+                   "ld 25, 464(3)\n\t"
+                   "ld 26, 472(3)\n\t"
+                   "ld 27, 480(3)\n\t"
+                   "ld 28, 488(3)\n\t"
+                   "ld 29, 496(3)\n\t"
+                   "ld 30, 504(3)\n\t"
+                   "ld 31, 512(3)\n\t"
+                   "ld 12, 544(3)\n\t" /* iar */
+                   "ld 0, 552(3)\n\t" /* lr */
+                   "mtlr 0\n\t"
+                   "ld 3, 288(3)\n\t" /* r3 */
+                   "mtctr 12\n\t"
+                   "bctr" )
+
+
+/***********************************************************************
+ *           get_server_context_flags
+ *
+ * Convert CPU-specific flags to generic server flags
+ */
+static unsigned int get_server_context_flags( DWORD flags )
+{
+    unsigned int ret = 0;
+
+    flags &= ~CONTEXT_POWERPC64;  /* get rid of CPU id */
+    if (flags & CONTEXT_CONTROL) ret |= SERVER_CTX_CONTROL;
+    if (flags & CONTEXT_INTEGER) ret |= SERVER_CTX_INTEGER;
+    if (flags & CONTEXT_FLOATING_POINT) ret |= SERVER_CTX_FLOATING_POINT;
+    if (flags & CONTEXT_DEBUG_REGISTERS) ret |= SERVER_CTX_DEBUG_REGISTERS;
+    return ret;
+}
+
+
+/***********************************************************************
+ *           copy_context
+ *
+ * Copy a register context according to the flags.
+ */
+static void copy_context( CONTEXT *to, const CONTEXT *from, DWORD flags )
+{
+    if (flags & CONTEXT_CONTROL)
+    {
+        to->Msr   = from->Msr;
+        to->Ctr   = from->Ctr;
+        to->Iar   = from->Iar;
+        to->Lr    = from->Lr;
+        to->Dar   = from->Dar;
+        to->Dsisr = from->Dsisr;
+        to->Trap  = from->Trap;
+    }
+    if (flags & CONTEXT_INTEGER)
+    {
+        to->Gpr0  = from->Gpr0;
+        to->Gpr1  = from->Gpr1;
+        to->Gpr2  = from->Gpr2;
+        to->Gpr3  = from->Gpr3;
+        to->Gpr4  = from->Gpr4;
+        to->Gpr5  = from->Gpr5;
+        to->Gpr6  = from->Gpr6;
+        to->Gpr7  = from->Gpr7;
+        to->Gpr8  = from->Gpr8;
+        to->Gpr9  = from->Gpr9;
+        to->Gpr10 = from->Gpr10;
+        to->Gpr11 = from->Gpr11;
+        to->Gpr12 = from->Gpr12;
+        to->Gpr13 = from->Gpr13;
+        to->Gpr14 = from->Gpr14;
+        to->Gpr15 = from->Gpr15;
+        to->Gpr16 = from->Gpr16;
+        to->Gpr17 = from->Gpr17;
+        to->Gpr18 = from->Gpr18;
+        to->Gpr19 = from->Gpr19;
+        to->Gpr20 = from->Gpr20;
+        to->Gpr21 = from->Gpr21;
+        to->Gpr22 = from->Gpr22;
+        to->Gpr23 = from->Gpr23;
+        to->Gpr24 = from->Gpr24;
+        to->Gpr25 = from->Gpr25;
+        to->Gpr26 = from->Gpr26;
+        to->Gpr27 = from->Gpr27;
+        to->Gpr28 = from->Gpr28;
+        to->Gpr29 = from->Gpr29;
+        to->Gpr30 = from->Gpr30;
+        to->Gpr31 = from->Gpr31;
+        to->Xer   = from->Xer;
+        to->Cr    = from->Cr;
+    }
+    if (flags & CONTEXT_FLOATING_POINT)
+    {
+        to->Fpr0  = from->Fpr0;
+        to->Fpr1  = from->Fpr1;
+        to->Fpr2  = from->Fpr2;
+        to->Fpr3  = from->Fpr3;
+        to->Fpr4  = from->Fpr4;
+        to->Fpr5  = from->Fpr5;
+        to->Fpr6  = from->Fpr6;
+        to->Fpr7  = from->Fpr7;
+        to->Fpr8  = from->Fpr8;
+        to->Fpr9  = from->Fpr9;
+        to->Fpr10 = from->Fpr10;
+        to->Fpr11 = from->Fpr11;
+        to->Fpr12 = from->Fpr12;
+        to->Fpr13 = from->Fpr13;
+        to->Fpr14 = from->Fpr14;
+        to->Fpr15 = from->Fpr15;
+        to->Fpr16 = from->Fpr16;
+        to->Fpr17 = from->Fpr17;
+        to->Fpr18 = from->Fpr18;
+        to->Fpr19 = from->Fpr19;
+        to->Fpr20 = from->Fpr20;
+        to->Fpr21 = from->Fpr21;
+        to->Fpr22 = from->Fpr22;
+        to->Fpr23 = from->Fpr23;
+        to->Fpr24 = from->Fpr24;
+        to->Fpr25 = from->Fpr25;
+        to->Fpr26 = from->Fpr26;
+        to->Fpr27 = from->Fpr27;
+        to->Fpr28 = from->Fpr28;
+        to->Fpr29 = from->Fpr29;
+        to->Fpr30 = from->Fpr30;
+        to->Fpr31 = from->Fpr31;
+        to->Fpscr = from->Fpscr;
+    }
+}
+
+
+/***********************************************************************
+ *           context_to_server
+ *
+ * Convert a register context to the server format.
+ */
+NTSTATUS context_to_server( context_t *to, const CONTEXT *from )
+{
+    DWORD flags = from->ContextFlags & ~CONTEXT_POWERPC64;  /* get rid of CPU id */
+
+    memset( to, 0, sizeof(*to) );
+    to->cpu = CPU_POWERPC64;
+
+    if (flags & CONTEXT_CONTROL)
+    {
+        to->flags |= SERVER_CTX_CONTROL;
+        to->ctl.powerpc64_regs.iar   = from->Iar;
+        to->ctl.powerpc64_regs.msr   = from->Msr;
+        to->ctl.powerpc64_regs.ctr   = from->Ctr;
+        to->ctl.powerpc64_regs.lr    = from->Lr;
+        to->ctl.powerpc64_regs.dar   = from->Dar;
+        to->ctl.powerpc64_regs.dsisr = from->Dsisr;
+        to->ctl.powerpc64_regs.trap  = from->Trap;
+    }
+    if (flags & CONTEXT_INTEGER)
+    {
+        to->flags |= SERVER_CTX_INTEGER;
+        to->integer.powerpc64_regs.gpr[0]  = from->Gpr0;
+        to->integer.powerpc64_regs.gpr[1]  = from->Gpr1;
+        to->integer.powerpc64_regs.gpr[2]  = from->Gpr2;
+        to->integer.powerpc64_regs.gpr[3]  = from->Gpr3;
+        to->integer.powerpc64_regs.gpr[4]  = from->Gpr4;
+        to->integer.powerpc64_regs.gpr[5]  = from->Gpr5;
+        to->integer.powerpc64_regs.gpr[6]  = from->Gpr6;
+        to->integer.powerpc64_regs.gpr[7]  = from->Gpr7;
+        to->integer.powerpc64_regs.gpr[8]  = from->Gpr8;
+        to->integer.powerpc64_regs.gpr[9]  = from->Gpr9;
+        to->integer.powerpc64_regs.gpr[10] = from->Gpr10;
+        to->integer.powerpc64_regs.gpr[11] = from->Gpr11;
+        to->integer.powerpc64_regs.gpr[12] = from->Gpr12;
+        to->integer.powerpc64_regs.gpr[13] = from->Gpr13;
+        to->integer.powerpc64_regs.gpr[14] = from->Gpr14;
+        to->integer.powerpc64_regs.gpr[15] = from->Gpr15;
+        to->integer.powerpc64_regs.gpr[16] = from->Gpr16;
+        to->integer.powerpc64_regs.gpr[17] = from->Gpr17;
+        to->integer.powerpc64_regs.gpr[18] = from->Gpr18;
+        to->integer.powerpc64_regs.gpr[19] = from->Gpr19;
+        to->integer.powerpc64_regs.gpr[20] = from->Gpr20;
+        to->integer.powerpc64_regs.gpr[21] = from->Gpr21;
+        to->integer.powerpc64_regs.gpr[22] = from->Gpr22;
+        to->integer.powerpc64_regs.gpr[23] = from->Gpr23;
+        to->integer.powerpc64_regs.gpr[24] = from->Gpr24;
+        to->integer.powerpc64_regs.gpr[25] = from->Gpr25;
+        to->integer.powerpc64_regs.gpr[26] = from->Gpr26;
+        to->integer.powerpc64_regs.gpr[27] = from->Gpr27;
+        to->integer.powerpc64_regs.gpr[28] = from->Gpr28;
+        to->integer.powerpc64_regs.gpr[29] = from->Gpr29;
+        to->integer.powerpc64_regs.gpr[30] = from->Gpr30;
+        to->integer.powerpc64_regs.gpr[31] = from->Gpr31;
+        to->integer.powerpc64_regs.xer     = from->Xer;
+        to->integer.powerpc64_regs.cr      = from->Cr;
+    }
+    if (flags & CONTEXT_FLOATING_POINT)
+    {
+        to->flags |= SERVER_CTX_FLOATING_POINT;
+        to->fp.powerpc64_regs.fpr[0]  = from->Fpr0;
+        to->fp.powerpc64_regs.fpr[1]  = from->Fpr1;
+        to->fp.powerpc64_regs.fpr[2]  = from->Fpr2;
+        to->fp.powerpc64_regs.fpr[3]  = from->Fpr3;
+        to->fp.powerpc64_regs.fpr[4]  = from->Fpr4;
+        to->fp.powerpc64_regs.fpr[5]  = from->Fpr5;
+        to->fp.powerpc64_regs.fpr[6]  = from->Fpr6;
+        to->fp.powerpc64_regs.fpr[7]  = from->Fpr7;
+        to->fp.powerpc64_regs.fpr[8]  = from->Fpr8;
+        to->fp.powerpc64_regs.fpr[9]  = from->Fpr9;
+        to->fp.powerpc64_regs.fpr[10] = from->Fpr10;
+        to->fp.powerpc64_regs.fpr[11] = from->Fpr11;
+        to->fp.powerpc64_regs.fpr[12] = from->Fpr12;
+        to->fp.powerpc64_regs.fpr[13] = from->Fpr13;
+        to->fp.powerpc64_regs.fpr[14] = from->Fpr14;
+        to->fp.powerpc64_regs.fpr[15] = from->Fpr15;
+        to->fp.powerpc64_regs.fpr[16] = from->Fpr16;
+        to->fp.powerpc64_regs.fpr[17] = from->Fpr17;
+        to->fp.powerpc64_regs.fpr[18] = from->Fpr18;
+        to->fp.powerpc64_regs.fpr[19] = from->Fpr19;
+        to->fp.powerpc64_regs.fpr[20] = from->Fpr20;
+        to->fp.powerpc64_regs.fpr[21] = from->Fpr21;
+        to->fp.powerpc64_regs.fpr[22] = from->Fpr22;
+        to->fp.powerpc64_regs.fpr[23] = from->Fpr23;
+        to->fp.powerpc64_regs.fpr[24] = from->Fpr24;
+        to->fp.powerpc64_regs.fpr[25] = from->Fpr25;
+        to->fp.powerpc64_regs.fpr[26] = from->Fpr26;
+        to->fp.powerpc64_regs.fpr[27] = from->Fpr27;
+        to->fp.powerpc64_regs.fpr[28] = from->Fpr28;
+        to->fp.powerpc64_regs.fpr[29] = from->Fpr29;
+        to->fp.powerpc64_regs.fpr[30] = from->Fpr30;
+        to->fp.powerpc64_regs.fpr[31] = from->Fpr31;
+        to->fp.powerpc64_regs.fpscr   = from->Fpscr;
+    }
+    return STATUS_SUCCESS;
+}
+
+
+/***********************************************************************
+ *           context_from_server
+ *
+ * Convert a register context from the server format.
+ */
+NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
+{
+    if (from->cpu != CPU_POWERPC64) return STATUS_INVALID_PARAMETER;
+
+    to->ContextFlags = CONTEXT_POWERPC64;
+    if (from->flags & SERVER_CTX_CONTROL)
+    {
+        to->ContextFlags |= CONTEXT_CONTROL;
+        to->Msr   = from->ctl.powerpc64_regs.msr;
+        to->Ctr   = from->ctl.powerpc64_regs.ctr;
+        to->Iar   = from->ctl.powerpc64_regs.iar;
+        to->Lr    = from->ctl.powerpc64_regs.lr;
+        to->Dar   = from->ctl.powerpc64_regs.dar;
+        to->Dsisr = from->ctl.powerpc64_regs.dsisr;
+        to->Trap  = from->ctl.powerpc64_regs.trap;
+    }
+    if (from->flags & SERVER_CTX_INTEGER)
+    {
+        to->ContextFlags |= CONTEXT_INTEGER;
+        to->Gpr0  = from->integer.powerpc64_regs.gpr[0];
+        to->Gpr1  = from->integer.powerpc64_regs.gpr[1];
+        to->Gpr2  = from->integer.powerpc64_regs.gpr[2];
+        to->Gpr3  = from->integer.powerpc64_regs.gpr[3];
+        to->Gpr4  = from->integer.powerpc64_regs.gpr[4];
+        to->Gpr5  = from->integer.powerpc64_regs.gpr[5];
+        to->Gpr6  = from->integer.powerpc64_regs.gpr[6];
+        to->Gpr7  = from->integer.powerpc64_regs.gpr[7];
+        to->Gpr8  = from->integer.powerpc64_regs.gpr[8];
+        to->Gpr9  = from->integer.powerpc64_regs.gpr[9];
+        to->Gpr10 = from->integer.powerpc64_regs.gpr[10];
+        to->Gpr11 = from->integer.powerpc64_regs.gpr[11];
+        to->Gpr12 = from->integer.powerpc64_regs.gpr[12];
+        to->Gpr13 = from->integer.powerpc64_regs.gpr[13];
+        to->Gpr14 = from->integer.powerpc64_regs.gpr[14];
+        to->Gpr15 = from->integer.powerpc64_regs.gpr[15];
+        to->Gpr16 = from->integer.powerpc64_regs.gpr[16];
+        to->Gpr17 = from->integer.powerpc64_regs.gpr[17];
+        to->Gpr18 = from->integer.powerpc64_regs.gpr[18];
+        to->Gpr19 = from->integer.powerpc64_regs.gpr[19];
+        to->Gpr20 = from->integer.powerpc64_regs.gpr[20];
+        to->Gpr21 = from->integer.powerpc64_regs.gpr[21];
+        to->Gpr22 = from->integer.powerpc64_regs.gpr[22];
+        to->Gpr23 = from->integer.powerpc64_regs.gpr[23];
+        to->Gpr24 = from->integer.powerpc64_regs.gpr[24];
+        to->Gpr25 = from->integer.powerpc64_regs.gpr[25];
+        to->Gpr26 = from->integer.powerpc64_regs.gpr[26];
+        to->Gpr27 = from->integer.powerpc64_regs.gpr[27];
+        to->Gpr28 = from->integer.powerpc64_regs.gpr[28];
+        to->Gpr29 = from->integer.powerpc64_regs.gpr[29];
+        to->Gpr30 = from->integer.powerpc64_regs.gpr[30];
+        to->Gpr31 = from->integer.powerpc64_regs.gpr[31];
+        to->Xer   = from->integer.powerpc64_regs.xer;
+        to->Cr    = from->integer.powerpc64_regs.cr;
+    }
+    if (from->flags & SERVER_CTX_FLOATING_POINT)
+    {
+        to->ContextFlags |= CONTEXT_FLOATING_POINT;
+        to->Fpr0  = from->fp.powerpc64_regs.fpr[0];
+        to->Fpr1  = from->fp.powerpc64_regs.fpr[1];
+        to->Fpr2  = from->fp.powerpc64_regs.fpr[2];
+        to->Fpr3  = from->fp.powerpc64_regs.fpr[3];
+        to->Fpr4  = from->fp.powerpc64_regs.fpr[4];
+        to->Fpr5  = from->fp.powerpc64_regs.fpr[5];
+        to->Fpr6  = from->fp.powerpc64_regs.fpr[6];
+        to->Fpr7  = from->fp.powerpc64_regs.fpr[7];
+        to->Fpr8  = from->fp.powerpc64_regs.fpr[8];
+        to->Fpr9  = from->fp.powerpc64_regs.fpr[9];
+        to->Fpr10 = from->fp.powerpc64_regs.fpr[10];
+        to->Fpr11 = from->fp.powerpc64_regs.fpr[11];
+        to->Fpr12 = from->fp.powerpc64_regs.fpr[12];
+        to->Fpr13 = from->fp.powerpc64_regs.fpr[13];
+        to->Fpr14 = from->fp.powerpc64_regs.fpr[14];
+        to->Fpr15 = from->fp.powerpc64_regs.fpr[15];
+        to->Fpr16 = from->fp.powerpc64_regs.fpr[16];
+        to->Fpr17 = from->fp.powerpc64_regs.fpr[17];
+        to->Fpr18 = from->fp.powerpc64_regs.fpr[18];
+        to->Fpr19 = from->fp.powerpc64_regs.fpr[19];
+        to->Fpr20 = from->fp.powerpc64_regs.fpr[20];
+        to->Fpr21 = from->fp.powerpc64_regs.fpr[21];
+        to->Fpr22 = from->fp.powerpc64_regs.fpr[22];
+        to->Fpr23 = from->fp.powerpc64_regs.fpr[23];
+        to->Fpr24 = from->fp.powerpc64_regs.fpr[24];
+        to->Fpr25 = from->fp.powerpc64_regs.fpr[25];
+        to->Fpr26 = from->fp.powerpc64_regs.fpr[26];
+        to->Fpr27 = from->fp.powerpc64_regs.fpr[27];
+        to->Fpr28 = from->fp.powerpc64_regs.fpr[28];
+        to->Fpr29 = from->fp.powerpc64_regs.fpr[29];
+        to->Fpr30 = from->fp.powerpc64_regs.fpr[30];
+        to->Fpr31 = from->fp.powerpc64_regs.fpr[31];
+        to->Fpscr = from->fp.powerpc64_regs.fpscr;
+    }
+    return STATUS_SUCCESS;
+}
+
+
+/***********************************************************************
+ *              NtSetContextThread  (NTDLL.@)
+ *              ZwSetContextThread  (NTDLL.@)
+ */
+NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
+{
+    NTSTATUS ret;
+    BOOL self;
+    context_t server_context;
+
+    context_to_server( &server_context, context );
+    ret = set_thread_context( handle, &server_context, &self );
+    if (self && ret == STATUS_SUCCESS) set_cpu_context( context );
+    return ret;
+}
+
+
+/***********************************************************************
+ *              NtGetContextThread  (NTDLL.@)
+ *              ZwGetContextThread  (NTDLL.@)
+ */
+NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
+{
+    NTSTATUS ret;
+    struct syscall_frame *frame = ppc64_thread_data()->syscall_frame;
+    DWORD needed_flags = context->ContextFlags & ~CONTEXT_POWERPC64;
+    BOOL self = (handle == GetCurrentThread());
+
+    FIXME("semi stub\n");
+
+    if (!self)
+    {
+        context_t server_context;
+        unsigned int server_flags = get_server_context_flags( context->ContextFlags );
+
+        if ((ret = get_thread_context( handle, &server_context, server_flags, &self ))) return ret;
+        if ((ret = context_from_server( context, &server_context ))) return ret;
+        needed_flags &= ~context->ContextFlags;
+    }
+
+    if (self)
+    {
+        if (needed_flags & CONTEXT_INTEGER)
+        {
+            context->Gpr0  = 0;
+            context->Gpr3  = 0;
+            context->Gpr4  = 0;
+            context->Gpr5  = 0;
+            context->Gpr6  = 0;
+            context->Gpr7  = 0;
+            context->Gpr8  = 0;
+            context->Gpr9  = 0;
+            context->Gpr10 = 0;
+            context->Gpr11 = 0;
+            context->Gpr12 = 0;
+            context->Gpr13 = 0;
+            context->Gpr14 = frame->r14;
+            context->Gpr15 = frame->r15;
+            context->Gpr16 = frame->r16;
+            context->Gpr17 = frame->r17;
+            context->Gpr18 = frame->r18;
+            context->Gpr19 = frame->r19;
+            context->Gpr20 = frame->r20;
+            context->Gpr21 = frame->r21;
+            context->Gpr22 = frame->r22;
+            context->Gpr23 = frame->r23;
+            context->Gpr24 = frame->r24;
+            context->Gpr25 = frame->r25;
+            context->Gpr26 = frame->r26;
+            context->Gpr27 = frame->r27;
+            context->Gpr28 = frame->r28;
+            context->Gpr29 = frame->r29;
+            context->Gpr30 = frame->r30;
+            context->Gpr31 = frame->r31;
+            context->ContextFlags |= CONTEXT_INTEGER;
+        }
+        if (needed_flags & CONTEXT_CONTROL)
+        {
+            context->Gpr1   = (DWORD64)&frame->thunk_r1;
+            context->Gpr2   = (DWORD64)&frame->thunk_r2;
+            context->Lr     = frame->ret_addr;
+            context->Iar    = frame->thunk_addr;
+            context->ContextFlags |= CONTEXT_CONTROL;
+        }
+        if (needed_flags & CONTEXT_FLOATING_POINT) FIXME( "floating point not supported\n" );
+        if (needed_flags & CONTEXT_DEBUG_REGISTERS) FIXME( "debug registers not supported\n" );
+    }
+    return STATUS_SUCCESS;
+}
+
+
+/***********************************************************************
+ *           setup_exception
+ *
+ * Setup the exception record and context on the thread stack.
+ */
+static EXCEPTION_RECORD *setup_exception( ucontext_t *sigcontext )
+{
+    struct stack_layout *stack;
+    DWORD exception_code = 0;
+
+    /* push the stack_layout structure */
+    stack = (struct stack_layout *)((GPR_sig(1, sigcontext) - sizeof(*stack)) & ~15);
+
+    stack->rec.ExceptionRecord  = NULL;
+    stack->rec.ExceptionCode    = exception_code;
+    stack->rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+    stack->rec.ExceptionAddress = (LPVOID)IAR_sig(sigcontext);
+    stack->rec.NumberParameters = 0;
+
+    save_context( &stack->context, sigcontext );
+    save_fpu( &stack->context, sigcontext );
+    return &stack->rec;
+}
+
+/***********************************************************************
+ *           call_user_apc
+ */
+void WINAPI call_user_apc( CONTEXT *context_ptr, ULONG_PTR ctx, ULONG_PTR arg1,
+                           ULONG_PTR arg2, PNTAPCFUNC func )
+{
+    CONTEXT context;
+
+    if (!context_ptr)
+    {
+        context.ContextFlags = CONTEXT_FULL;
+        NtGetContextThread( GetCurrentThread(), &context );
+        context.Gpr3 = STATUS_USER_APC;
+        context_ptr = &context;
+    }
+    pKiUserApcDispatcher( context_ptr, ctx, arg1, arg2, func );
+}
+
+/***********************************************************************
+ *           call_raise_user_exception_dispatcher
+ */
+__ASM_GLOBAL_FUNC( call_raise_user_exception_dispatcher,
+                   "mr 5, 3\n\t"  /* dispatcher */
+                   "lis 12, " __ASM_NAME("call_user_exception_dispatcher") "@highest\n\t"
+                   "ori 12, 12, " __ASM_NAME("call_user_exception_dispatcher") "@higher\n\t"
+                   "rldicr 12, 12, 32, 31\n\t"
+                   "oris 12, 12, " __ASM_NAME("call_user_exception_dispatcher") "@high\n\t"
+                   "ori 12, 12, " __ASM_NAME("call_user_exception_dispatcher") "@l\n\t"
+                   "mtctr 12\n\t"
+                   "bctr" )
+
+
+/***********************************************************************
+ *           call_user_exception_dispatcher
+ */
+__ASM_GLOBAL_FUNC( call_user_exception_dispatcher,
+                   "mr 14, 3\n\t"
+                   "mr 15, 4\n\t"
+                   "mr 16, 5\n\t"
+                   "lis 12, " __ASM_NAME("NtCurrentTeb") "@highest\n\t"
+                   "ori 12, 12, " __ASM_NAME("NtCurrentTeb") "@higher\n\t"
+                   "rldicr 12, 12, 32, 31\n\t"
+                   "oris 12, 12, " __ASM_NAME("NtCurrentTeb") "@high\n\t"
+                   "ori 12, 12, " __ASM_NAME("NtCurrentTeb") "@l\n\t"
+                   "mtctr 12\n\t"
+                   "bctrl\n\t"
+                   "ld 8, 0x2f8(3)\n\t"             /* ppc64_thread_data()->syscall_frame */
+                   "ld 9, 96(8)\n\t"               /* frame->prev_frame */
+                   "std 9, 0x2f8(3)\n\t"
+                   "mr 3, 14\n\t"
+                   "mr 4, 15\n\t"
+                   "mr 5, 16\n\t"
+                   "ld 14, 112(8)\n\t"
+                   "ld 15, 120(8)\n\t"
+                   "ld 16, 128(8)\n\t"
+                   "ld 17, 136(8)\n\t"
+                   "ld 18, 144(8)\n\t"
+                   "ld 19, 152(8)\n\t"
+                   "ld 20, 160(8)\n\t"
+                   "ld 21, 168(8)\n\t"
+                   "ld 22, 176(8)\n\t"
+                   "ld 23, 184(8)\n\t"
+                   "ld 24, 192(8)\n\t"
+                   "ld 25, 200(8)\n\t"
+                   "ld 26, 208(8)\n\t"
+                   "ld 27, 216(8)\n\t"
+                   "ld 28, 224(8)\n\t"
+                   "ld 29, 232(8)\n\t"
+                   "ld 20, 240(8)\n\t"
+                   "ld 31, 248(8)\n\t"
+                   "addi 1, 8, 256\n\t"
+                   "mr 12, 5\n\t"
+                   "mtctr 12\n\t"
+                   "bctr" )
+
+/**********************************************************************
+ *		segv_handler
+ *
+ * Handler for SIGSEGV and related errors.
+ */
+static void segv_handler( int signal, siginfo_t *info, void *ucontext )
+{
+    EXCEPTION_RECORD *rec;
+    ucontext_t *context = ucontext;
+
+#if 0
+    /* check for page fault inside the thread stack */
+    if (signal == SIGSEGV)
+    {
+        switch (virtual_handle_stack_fault( info->si_addr ))
+        {
+        case 1:  /* handled */
+            return;
+        case -1:  /* overflow */
+            rec = setup_exception( context );
+            rec->ExceptionCode = EXCEPTION_STACK_OVERFLOW;
+            return;
+        }
+    }
+#endif
+
+    rec = setup_exception( context );
+    if (rec->ExceptionCode == EXCEPTION_STACK_OVERFLOW) return;
+
+    switch(signal)
+    {
+    case SIGILL:   /* Invalid opcode exception */
+        rec->ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+        break;
+    case SIGSEGV:  /* Segmentation fault */
+        rec->NumberParameters = 2;
+        rec->ExceptionInformation[0] = 0; /* FIXME ? */
+        rec->ExceptionInformation[1] = (ULONG_PTR)info->si_addr;
+        if (!(rec->ExceptionCode = virtual_handle_fault( (void *)rec->ExceptionInformation[1],
+                                                         rec->ExceptionInformation[0], FALSE )))
+            return;
+        break;
+    case SIGBUS:  /* Alignment check exception */
+        rec->ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
+        break;
+    default:
+        ERR("Got unexpected signal %i\n", signal);
+        rec->ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
+        break;
+    }
+}
+
+
+/**********************************************************************
+ *		trap_handler
+ *
+ * Handler for SIGTRAP.
+ */
+static void trap_handler( int signal, siginfo_t *info, void *ucontext )
+{
+    EXCEPTION_RECORD rec;
+    CONTEXT context;
+    NTSTATUS status;
+
+    save_context( &context, ucontext );
+
+    rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+    rec.ExceptionRecord  = NULL;
+    rec.ExceptionAddress = (LPVOID)context.Iar;
+    rec.NumberParameters = 0;
+
+    /* FIXME: check if we might need to modify PC */
+    switch (info->si_code & 0xffff)
+    {
+#ifdef TRAP_BRKPT
+    case TRAP_BRKPT:
+        rec.ExceptionCode = EXCEPTION_BREAKPOINT;
+    break;
+#endif
+#ifdef TRAP_TRACE
+    case TRAP_TRACE:
+        rec.ExceptionCode = EXCEPTION_SINGLE_STEP;
+    break;
+#endif
+    default:
+        FIXME("Unhandled SIGTRAP/%x\n", info->si_code);
+        break;
+    }
+    status = NtRaiseException( &rec, &context, TRUE );
+    if (status) RtlRaiseStatus( status );
+    restore_context( &context, ucontext );
+}
+
+
+/**********************************************************************
+ *		fpe_handler
+ *
+ * Handler for SIGFPE.
+ */
+static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
+{
+    EXCEPTION_RECORD rec;
+    CONTEXT context;
+    NTSTATUS status;
+
+    save_fpu( &context, sigcontext );
+    save_context( &context, sigcontext );
+
+    switch (siginfo->si_code & 0xffff )
+    {
+#ifdef FPE_FLTSUB
+    case FPE_FLTSUB:
+        rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
+        break;
+#endif
+#ifdef FPE_INTDIV
+    case FPE_INTDIV:
+        rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
+        break;
+#endif
+#ifdef FPE_INTOVF
+    case FPE_INTOVF:
+        rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
+        break;
+#endif
+#ifdef FPE_FLTDIV
+    case FPE_FLTDIV:
+        rec.ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO;
+        break;
+#endif
+#ifdef FPE_FLTOVF
+    case FPE_FLTOVF:
+        rec.ExceptionCode = EXCEPTION_FLT_OVERFLOW;
+        break;
+#endif
+#ifdef FPE_FLTUND
+    case FPE_FLTUND:
+        rec.ExceptionCode = EXCEPTION_FLT_UNDERFLOW;
+        break;
+#endif
+#ifdef FPE_FLTRES
+    case FPE_FLTRES:
+        rec.ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT;
+        break;
+#endif
+#ifdef FPE_FLTINV
+    case FPE_FLTINV:
+#endif
+    default:
+        rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
+        break;
+    }
+    rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+    rec.ExceptionRecord  = NULL;
+    rec.ExceptionAddress = (LPVOID)context.Iar;
+    rec.NumberParameters = 0;
+    status = NtRaiseException( &rec, &context, TRUE );
+    if (status) RtlRaiseStatus( status );
+
+    restore_context( &context, sigcontext );
+    restore_fpu( &context, sigcontext );
+}
+
+
+/**********************************************************************
+ *		int_handler
+ *
+ * Handler for SIGINT.
+ */
+static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext )
+{
+    EXCEPTION_RECORD rec;
+    CONTEXT context;
+    NTSTATUS status;
+
+    save_context( &context, sigcontext );
+    rec.ExceptionCode    = CONTROL_C_EXIT;
+    rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+    rec.ExceptionRecord  = NULL;
+    rec.ExceptionAddress = (LPVOID)context.Iar;
+    rec.NumberParameters = 0;
+    status = NtRaiseException( &rec, &context, TRUE );
+    if (status) RtlRaiseStatus( status );
+    restore_context( &context, sigcontext );
+}
+
+
+/**********************************************************************
+ *		abrt_handler
+ *
+ * Handler for SIGABRT.
+ */
+static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext )
+{
+    EXCEPTION_RECORD rec;
+    CONTEXT context;
+    NTSTATUS status;
+
+    save_context( &context, sigcontext );
+    rec.ExceptionCode    = EXCEPTION_WINE_ASSERTION;
+    rec.ExceptionFlags   = EH_NONCONTINUABLE;
+    rec.ExceptionRecord  = NULL;
+    rec.ExceptionAddress = (LPVOID)context.Iar;
+    rec.NumberParameters = 0;
+    status = NtRaiseException( &rec, &context, TRUE );
+    if (status) RtlRaiseStatus( status );
+    restore_context( &context, sigcontext );
+}
+
+
+/**********************************************************************
+ *		quit_handler
+ *
+ * Handler for SIGQUIT.
+ */
+static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext )
+{
+    abort_thread(0);
+}
+
+
+/**********************************************************************
+ *		usr1_handler
+ *
+ * Handler for SIGUSR1, used to signal a thread that it got suspended.
+ */
+static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
+{
+    CONTEXT context;
+
+    save_context( &context, sigcontext );
+    wait_suspend( &context );
+    restore_context( &context, sigcontext );
+}
+
+
+/**********************************************************************
+ *           get_thread_ldt_entry
+ */
+NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+/******************************************************************************
+ *           NtSetLdtEntries   (NTDLL.@)
+ *           ZwSetLdtEntries   (NTDLL.@)
+ */
+NTSTATUS WINAPI NtSetLdtEntries( ULONG sel1, LDT_ENTRY entry1, ULONG sel2, LDT_ENTRY entry2 )
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+
+/**********************************************************************
+ *             signal_init_threading
+ */
+void signal_init_threading(void)
+{
+    pthread_key_create( &teb_key, NULL );
+}
+
+
+/**********************************************************************
+ *             signal_alloc_thread
+ */
+NTSTATUS signal_alloc_thread( TEB *teb )
+{
+    return STATUS_SUCCESS;
+}
+
+
+/**********************************************************************
+ *             signal_free_thread
+ */
+void signal_free_thread( TEB *teb )
+{
+}
+
+
+/**********************************************************************
+ *		signal_init_thread
+ */
+void signal_init_thread( TEB *teb )
+{
+    pthread_setspecific( teb_key, teb );
+}
+
+
+/**********************************************************************
+ *		signal_init_process
+ */
+void signal_init_process(void)
+{
+    struct sigaction sig_act;
+
+    sig_act.sa_mask = server_block_set;
+    sig_act.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;
+
+    sig_act.sa_sigaction = int_handler;
+    if (sigaction( SIGINT, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = fpe_handler;
+    if (sigaction( SIGFPE, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = abrt_handler;
+    if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = quit_handler;
+    if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = usr1_handler;
+    if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = trap_handler;
+    if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error;
+    sig_act.sa_sigaction = segv_handler;
+    if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error;
+    if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error;
+    if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error;
+    return;
+
+ error:
+    perror("sigaction");
+    exit(1);
+}
+
+/***********************************************************************
+ *           init_thread_context
+ */
+static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
+{
+    context->Gpr3 = (DWORD64)entry;
+    context->Gpr4 = (DWORD64)arg;
+    context->Gpr1 = (DWORD64)NtCurrentTeb()->Tib.StackBase;
+    context->Iar  = (DWORD64)relay;
+}
+
+#if 0
+extern int wine_call_on_stack_obsolete( int (*func)(void *), void *arg, void *stack );
+__ASM_GLOBAL_FUNC( wine_call_on_stack_obsolete,
+                   "mflr 0\n\t"           /* get return address */
+                   "std 0, 16(1)\n\t"     /* save return address on stack */
+                   "subi 5, 5, 112\n\t"   /* reserve space on new stack */
+                   "std 1, 48(5)\n\t"     /* store old sp */
+                   "mr 12, 3\n\t"         /* func -> r12: ppc64 v2 requires r12 == ctr before bctr for TOC setup */
+                   "mtctr 12\n\t"         /* func -> ctr */
+                   "mr 3, 4\n\t"          /* args -> function param 1 (r3) */
+                   "mr 1, 5\n\t"          /* stack */
+                   "li 0, 0\n\t"          /* zero */
+                   "std 0, 0(1)\n\t"      /* bottom of stack */
+                   "stdu 1, -112(1)\n\t"  /* create a frame for this function */
+                   "bctrl\n\t"            /* call ctr */
+                   "ld 1, 160(1)\n\t"     /* fetch old sp */
+                   "ld 0, 16(1)\n\t"      /* fetch return address */
+                   "mtlr 0\n\t"           /* return address -> lr */
+                   "blr")                 /* return */
+void DECLSPEC_NORETURN wine_switch_to_stack_obsolete( void (*func)(void *), void *arg, void *stack )
+{
+    wine_call_on_stack_obsolete( (int (*)(void *))func, arg, stack );
+    abort();
+}
+typedef void (WINAPI *thread_start_func)(LPTHREAD_START_ROUTINE,void *);
+struct startup_info
+{
+    thread_start_func      start;
+    PRTL_THREAD_START_ROUTINE entry;
+    void                  *arg;
+    BOOL                   suspend;
+};
+/***********************************************************************
+ *           thread_startup
+ */
+static void thread_startup( void *param )
+{
+    CONTEXT context = { 0 };
+    struct startup_info *info = param;
+
+    /* build the initial context */
+    context.ContextFlags = CONTEXT_FULL;
+    context.Gpr1 = (DWORD64)NtCurrentTeb()->Tib.StackBase;
+    context.Gpr3 = (DWORD64)info->entry;
+    context.Gpr4 = (DWORD64)info->arg;
+    context.Iar  = (DWORD64)info->start;
+
+    if (info->suspend) wait_suspend( &context );
+    pLdrInitializeThunk( &context, (void **)&context.Gpr3, 0, 0 );
+
+    ((thread_start_func)context.Iar)( (LPTHREAD_START_ROUTINE)context.Gpr3, (void *)context.Gpr4 );
+}
+/***********************************************************************
+ *           call_thread_entry_point
+ */
+static void WINAPI call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg )
+{
+    RtlExitUserThread( entry( arg ));
+    /*__TRY
+    {
+        RtlExitUserThread( entry( arg ));
+    }
+    __EXCEPT(call_unhandled_exception_filter)
+    {
+        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
+    }
+    __ENDTRY*/
+    abort();  /* should not be reached */
+}
+#endif
+
+/***********************************************************************
+ *           attach_thread
+ */
+PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
+                                        BOOL suspend, void *relay )
+{
+    CONTEXT *ctx;
+
+    if (suspend)
+    {
+        CONTEXT context = { CONTEXT_ALL };
+
+        init_thread_context( &context, entry, arg, relay );
+        wait_suspend( &context );
+        ctx = (CONTEXT *)((ULONG_PTR)context.Gpr1 & ~15) - 1;
+        *ctx = context;
+    }
+    else
+    {
+        ctx = (CONTEXT *)NtCurrentTeb()->Tib.StackBase - 1;
+        init_thread_context( ctx, entry, arg, relay );
+    }
+    pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL );
+    ctx->ContextFlags = CONTEXT_FULL;
+    pLdrInitializeThunk( ctx, (void **)&ctx->Gpr3, 0, 0 );
+    return ctx;
+}
+
+
+/***********************************************************************
+ *           signal_start_thread
+ */
+__ASM_GLOBAL_FUNC( signal_start_thread,
+                   "mflr 0\n\t"
+                   "std 0, 16(1)\n\t"
+                   /* store exit frame */
+                   "addi 8, 7, 0x200\n\t"
+                   "addi 8, 8, 0x0f0\n\t"
+                   "std 1, 0(8)\n\t" /* ppc64_thread_data()->exit_frame */ /* FIXME: segv */
+                   /* switch to thread stack */
+                   "ld 8, 8(7)\n\t"  /* teb->Tib.StackBase */
+                   "addi 1, 8, -0x1000\n\t"
+                   /* attach dlls */
+                   "lis 12, " __ASM_NAME("attach_thread") "@highest\n\t"
+                   "ori 12, 12, " __ASM_NAME("attach_thread") "@higher\n\t"
+                   "rldicr 12, 12, 32, 31\n\t"
+                   "oris 12, 12, " __ASM_NAME("attach_thread") "@high\n\t"
+                   "ori 12, 12, " __ASM_NAME("attach_thread") "@l\n\t"
+                   "mtctr 12\n\t"
+                   "bctrl\n\t"
+                   "ori 0, 0, 0\n\t"
+                   "mr 1, 3\n\t"
+                   /* clear the stack */
+                   "rlwinm 3, 3, 0, 0, 15\n\t" /* round down to page size */
+                   "lis 12, " __ASM_NAME("virtual_clear_thread_stack") "@highest\n\t"
+                   "ori 12, 12, " __ASM_NAME("virtual_clear_thread_stack") "@higher\n\t"
+                   "rldicr 12, 12, 32, 31\n\t"
+                   "oris 12, 12, " __ASM_NAME("virtual_clear_thread_stack") "@high\n\t"
+                   "ori 12, 12, " __ASM_NAME("virtual_clear_thread_stack") "@l\n\t"
+                   "mtctr 12\n\t"
+                   "bctrl\n\t"
+                   "ori 0, 0, 0\n\t"
+                   /* switch to the initial context */
+                   "mr 3, 1\n\t"
+                   /* FIXME: ignoring floatingpointunit */
+                   "li 4, 1\n\t"
+                   "lis 12, " __ASM_NAME("NtContinue") "@highest\n\t"
+                   "ori 12, 12, " __ASM_NAME("NtContinue") "@higher\n\t"
+                   "rldicr 12, 12, 32, 31\n\t"
+                   "oris 12, 12, " __ASM_NAME("NtContinue") "@high\n\t"
+                   "ori 12, 12, " __ASM_NAME("NtContinue") "@l\n\t"
+                   "mtctr 12\n\t"
+                   "bctr" )
+
+/*
+void signal_start_thread( PRTL_THREAD_START_ROUTINE entry, void *arg, BOOL suspend, void *relay, TEB *teb )
+{
+    struct startup_info info = { call_thread_entry_point, entry, arg, suspend };
+    FIXME( "stub?\n" );
+    wine_switch_to_stack_obsolete( thread_startup, &info, teb->Tib.StackBase );
+}
+*/
+
+extern void DECLSPEC_NORETURN call_thread_exit_func( int status, void (*func)(int), TEB *teb );
+__ASM_GLOBAL_FUNC( call_thread_exit_func,
+                   "li 7, 0\n\t"
+                   "addi 8, 5, 0x200\n\t"
+                   "addi 8, 8, 0x0f0\n\t"
+                   "ld 6, 0(8)\n\t"
+                   "std 7, 0(8)\n\t"
+                   "cmpdi cr7, 6, 0\n\t"
+                   "beq cr7, 1f\n\t"
+                   "mr 1, 6\n"
+                   "1:\tmr 12, 4\n\t"
+                   "mtctr 12\n\t"
+                   "bctr" )
+
+/***********************************************************************
+ *           signal_exit_thread
+ */
+void signal_exit_thread( int status, void (*func)(int) )
+{
+    call_thread_exit_func( status, func, NtCurrentTeb() );
+}
+
+
+/**********************************************************************
+ *           NtCurrentTeb   (NTDLL.@)
+ */
+TEB * WINAPI NtCurrentTeb(void)
+{
+    return pthread_getspecific( teb_key );
+}
+
+#endif  /* __aarch64__ */
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c
index 3148f23df36..b4a36071b9c 100644
--- a/dlls/ntdll/unix/system.c
+++ b/dlls/ntdll/unix/system.c
@@ -473,6 +473,13 @@ static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info )
     info->Architecture = PROCESSOR_ARCHITECTURE_ARM64;
 }
 
+#elif defined(__powerpc64__)
+
+static void get_cpuinfo( SYSTEM_CPU_INFORMATION *info )
+{
+    FIXME("CPU Feature detection not implemented.\n");
+}
+
 #endif /* End architecture specific feature detection for CPUs */
 
 /******************************************************************
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 397211957bd..a9f36c7b838 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -38,6 +38,8 @@ static const enum cpu_type client_cpu = CPU_x86_64;
 static const enum cpu_type client_cpu = CPU_ARM;
 #elif defined(__aarch64__)
 static const enum cpu_type client_cpu = CPU_ARM64;
+#elif defined(__powerpc64__)
+static const enum cpu_type client_cpu = CPU_POWERPC64;
 #endif
 
 struct debug_info
@@ -70,7 +72,11 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void)
     return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch;
 }
 
+#if defined(__powerpc64__)
+static const SIZE_T page_size = 0x10000;
+#else
 static const SIZE_T page_size = 0x1000;
+#endif
 static const SIZE_T signal_stack_mask = 0xffff;
 #ifdef _WIN64
 static const SIZE_T teb_size = 0x2000;
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index a2fea588834..8c85a48bc34 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -2454,6 +2454,8 @@ NTSTATUS virtual_map_ntdll( int fd, void **module )
         nt.FileHeader.Machine != IMAGE_FILE_MACHINE_ARMNT) return STATUS_INVALID_IMAGE_FORMAT;
 #elif defined(__aarch64__)
     if (nt.FileHeader.Machine != IMAGE_FILE_MACHINE_ARM64) return STATUS_INVALID_IMAGE_FORMAT;
+#elif defined(__powerpc64__)
+    if (nt.FileHeader.Machine != IMAGE_FILE_MACHINE_POWERPC64) return STATUS_INVALID_IMAGE_FORMAT;
 #endif
 
     base  = (void *)nt.OptionalHeader.ImageBase;
-- 
2.25.1




More information about the wine-devel mailing list