[PATCH 3/4] kernel32: Implement CopyContext().

Paul Gofman pgofman at codeweavers.com
Tue Sep 1 04:21:45 CDT 2020


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 .../api-ms-win-core-xstate-l1-1-0.spec        |   2 +-
 .../api-ms-win-core-xstate-l2-1-0.spec        |   2 +-
 dlls/kernel32/kernel32.spec                   |   2 +-
 dlls/kernelbase/kernelbase.spec               |   2 +-
 dlls/kernelbase/memory.c                      |  56 +++
 dlls/ntdll/exception.c                        |  85 +++-
 dlls/ntdll/ntdll.spec                         |   1 +
 dlls/ntdll/tests/exception.c                  | 365 ++++++++++++++++++
 dlls/ntoskrnl.exe/ntoskrnl.exe.spec           |   1 +
 include/ddk/wdm.h                             |   1 +
 include/winbase.h                             |   1 +
 11 files changed, 512 insertions(+), 6 deletions(-)

diff --git a/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec b/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
index 4abed7d3252..bfb2f68aa40 100644
--- a/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
+++ b/dlls/api-ms-win-core-xstate-l1-1-0/api-ms-win-core-xstate-l1-1-0.spec
@@ -1,4 +1,4 @@
-@ stub RtlCopyExtendedContext
+@ stdcall RtlCopyExtendedContext(ptr long ptr) ntdll.RtlCopyExtendedContext
 @ stdcall RtlGetEnabledExtendedFeatures(int64) ntdll.RtlGetEnabledExtendedFeatures
 @ stdcall RtlGetExtendedContextLength(long ptr) ntdll.RtlGetExtendedContextLength
 @ stdcall RtlGetExtendedFeaturesMask(ptr) ntdll.RtlGetExtendedFeaturesMask
diff --git a/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec b/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
index 9c804b28864..a0baac7d4c7 100644
--- a/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
+++ b/dlls/api-ms-win-core-xstate-l2-1-0/api-ms-win-core-xstate-l2-1-0.spec
@@ -1,4 +1,4 @@
-@ stub CopyContext
+@ stdcall -arch=i386,x86_64 CopyContext(ptr long ptr) kernel32.CopyContext
 @ stdcall -ret64 -arch=i386,x86_64 GetEnabledXStateFeatures() kernel32.GetEnabledXStateFeatures
 @ stdcall -arch=i386,x86_64 GetXStateFeaturesMask(ptr ptr) kernel32.GetXStateFeaturesMask
 @ stdcall -arch=i386,x86_64 InitializeContext(ptr long ptr ptr) kernel32.InitializeContext
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index d357d32c23e..38eb1d5ece7 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -256,7 +256,7 @@
 @ stdcall -import ConvertThreadToFiber(ptr)
 @ stdcall -import ConvertThreadToFiberEx(ptr long)
 @ stdcall ConvertToGlobalHandle(long)
-# @ stub CopyContext
+@ stdcall -import -arch=i386,x86_64 CopyContext(ptr long ptr)
 @ stdcall CopyFileA(str str long)
 @ stdcall CopyFileExA (str str ptr ptr ptr long)
 @ stdcall -import CopyFileExW(wstr wstr ptr ptr ptr long)
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index e8e974d9c15..a794e33faf3 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -165,7 +165,7 @@
 @ stdcall ConvertThreadToFiber(ptr)
 @ stdcall ConvertThreadToFiberEx(ptr long)
 @ stdcall ConvertToAutoInheritPrivateObjectSecurity(ptr ptr ptr ptr long ptr)
-# @ stub CopyContext
+@ stdcall -arch=i386,x86_64 CopyContext(ptr long ptr)
 # @ stub CopyFile2
 @ stdcall CopyFileExW(wstr wstr ptr ptr ptr long)
 @ stdcall CopyFileW(wstr wstr long)
diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c
index 38aa2c78410..4ead38f2bc0 100644
--- a/dlls/kernelbase/memory.c
+++ b/dlls/kernelbase/memory.c
@@ -1233,6 +1233,62 @@ BOOL WINAPI InitializeContext( void *buffer, DWORD context_flags, CONTEXT **cont
 {
     return InitializeContext2( buffer, context_flags, context, length, ~(ULONG64)0 );
 }
+
+/***********************************************************************
+ *           CopyContext                       (kernelbase.@)
+ */
+BOOL WINAPI CopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src )
+{
+    DWORD context_size, arch_flag, flags_offset, dst_flags, src_flags;
+    static const DWORD arch_mask = 0x110000;
+    NTSTATUS status;
+    BYTE *d, *s;
+
+    TRACE("dst %p, context_flags %#x, src %p.\n", dst, context_flags, src);
+
+    if (context_flags & 0x40 && !RtlGetEnabledExtendedFeatures( ~(ULONG64)0 ))
+    {
+        SetLastError(ERROR_NOT_SUPPORTED);
+        return FALSE;
+    }
+
+    arch_flag = context_flags & arch_mask;
+
+    switch (arch_flag)
+    {
+        case  0x10000: context_size = 0x2cc; flags_offset =    0; break;
+        case 0x100000: context_size = 0x4d0; flags_offset = 0x30; break;
+        default:
+            SetLastError( ERROR_INVALID_PARAMETER );
+            return FALSE;
+    }
+
+    d = (BYTE *)dst;
+    s = (BYTE *)src;
+    dst_flags = *(DWORD *)(d + flags_offset);
+    src_flags = *(DWORD *)(s + flags_offset);
+
+    if ((dst_flags & arch_mask) != arch_flag
+            || (src_flags & arch_mask) != arch_flag)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    context_flags &= src_flags;
+
+    if (context_flags & ~dst_flags & 0x40)
+    {
+        SetLastError(ERROR_MORE_DATA);
+        return FALSE;
+    }
+
+    if ((status = RtlCopyExtendedContext( (CONTEXT_EX *)(d + context_size), context_flags,
+            (CONTEXT_EX *)(s + context_size) )))
+        return set_ntstatus( status );
+
+    return TRUE;
+}
 #endif
 
 
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index e022e2c2cd1..17bbb26f9e8 100644
--- a/dlls/ntdll/exception.c
+++ b/dlls/ntdll/exception.c
@@ -666,6 +666,24 @@ ULONG64 WINAPI RtlGetEnabledExtendedFeatures(ULONG64 feature_mask)
     return user_shared_data->XState.EnabledFeatures & feature_mask;
 }
 
+struct context_copy_range
+{
+    ULONG start;
+    ULONG flag;
+};
+
+static const struct context_copy_range copy_ranges_amd64[] =
+{
+    {0x38, 0x1}, {0x3a, 0x4}, { 0x42, 0x1}, { 0x48, 0x10}, { 0x78,  0x2}, { 0x98, 0x1},
+    {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0,    0}, {0x4b0, 0x10}, {0x4d0,   0}
+};
+
+static const struct context_copy_range copy_ranges_x86[] =
+{
+    {  0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0},
+    {0x2cc,    0},
+};
+
 static const struct context_parameters
 {
     ULONG arch_flag;
@@ -676,11 +694,12 @@ static const struct context_parameters
     ULONG alignment;       /* Used when computing size of context. */
     ULONG true_alignment;  /* Used for actual alignment. */
     ULONG flags_offset;
+    const struct context_copy_range *copy_ranges;
 }
 arch_context_paramaters[] =
 {
-    {0x00100000, 0xd810005f, 0x4d0, 0x4d0, 0x20, 7, 0xf, 0x30},
-    {0x00010000, 0xd801007f, 0x2cc,  0xcc, 0x18, 3, 0x3,    0},
+    {0x00100000, 0xd810005f, 0x4d0, 0x4d0, 0x20, 7, 0xf, 0x30, copy_ranges_amd64},
+    {0x00010000, 0xd801007f, 0x2cc,  0xcc, 0x18, 3, 0x3,    0,   copy_ranges_x86},
 };
 
 static const struct context_parameters *context_get_parameters( ULONG context_flags )
@@ -882,3 +901,65 @@ ULONG64 WINAPI RtlGetExtendedFeaturesMask( CONTEXT_EX *context_ex )
 
     return xs->Mask & ~(ULONG64)3;
 }
+
+
+/**********************************************************************
+ *              RtlCopyExtendedContext      (NTDLL.@)
+ */
+NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src )
+{
+    const struct context_copy_range *range;
+    const struct context_parameters *p;
+    XSTATE *dst_xs, *src_xs;
+    ULONG64 feature_mask;
+    unsigned int start;
+    BYTE *d, *s;
+
+    TRACE( "dst %p, context_flags %#x, src %p.\n", dst, context_flags, src );
+
+    if (!(p = context_get_parameters( context_flags )))
+        return STATUS_INVALID_PARAMETER;
+
+    if (!(feature_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0 )) && context_flags & 0x40)
+        return STATUS_NOT_SUPPORTED;
+
+    d = RtlLocateLegacyContext( dst, NULL );
+    s = RtlLocateLegacyContext( src, NULL );
+
+    *((ULONG *)(d + p->flags_offset)) |= context_flags;
+
+    start = 0;
+    range = p->copy_ranges;
+    do
+    {
+        if (range->flag & context_flags)
+        {
+            if (!start)
+                start = range->start;
+        }
+        else if (start)
+        {
+            memcpy( d + start, s + start, range->start - start );
+            start = 0;
+        }
+    }
+    while (range++->start != p->context_size);
+
+    if (!(context_flags & 0x40))
+        return STATUS_SUCCESS;
+
+    if (dst->XState.Length < offsetof(XSTATE, YmmContext))
+        return STATUS_BUFFER_OVERFLOW;
+
+    dst_xs = (XSTATE *)((BYTE *)dst + dst->XState.Offset);
+    src_xs = (XSTATE *)((BYTE *)src + src->XState.Offset);
+
+    memset(dst_xs, 0, offsetof(XSTATE, YmmContext));
+    dst_xs->Mask = (src_xs->Mask & ~(ULONG64)3) & feature_mask;
+    dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled
+            ? ((ULONG64)1 << 63) | (src_xs->CompactionMask & feature_mask) : 0;
+
+    if (dst_xs->Mask & 4 && src->XState.Length >= sizeof(XSTATE) && dst->XState.Length >= sizeof(XSTATE))
+        memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
+    return STATUS_SUCCESS;
+}
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index c7083e0299c..9aa20034fa4 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -525,6 +525,7 @@
 @ stub RtlConvertUiListToApiList
 @ stdcall -arch=win32 -ret64 RtlConvertUlongToLargeInteger(long)
 # @ stub RtlConvertVariantToProperty
+@ stdcall RtlCopyExtendedContext(ptr long ptr)
 @ stdcall RtlCopyLuid(ptr ptr)
 @ stdcall RtlCopyLuidAndAttributesArray(long ptr ptr)
 @ stdcall -arch=x86_64 RtlCopyMemory(ptr ptr long)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 5d0857476ea..301b86e8d3c 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -51,6 +51,7 @@ static NTSTATUS  (WINAPI *pRtlGetExtendedContextLength2)(ULONG context_flags, UL
 static NTSTATUS  (WINAPI *pRtlInitializeExtendedContext)(void *context, ULONG context_flags, CONTEXT_EX **context_ex);
 static NTSTATUS  (WINAPI *pRtlInitializeExtendedContext2)(void *context, ULONG context_flags, CONTEXT_EX **context_ex,
         ULONG64 compaction_mask);
+static NTSTATUS  (WINAPI *pRtlCopyExtendedContext)(CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src);
 static void *    (WINAPI *pRtlLocateExtendedFeature)(CONTEXT_EX *context_ex, ULONG feature_id, ULONG *length);
 static void *    (WINAPI *pRtlLocateLegacyContext)(CONTEXT_EX *context_ex, ULONG *length);
 static void      (WINAPI *pRtlSetExtendedFeaturesMask)(CONTEXT_EX *context_ex, ULONG64 feature_mask);
@@ -71,6 +72,7 @@ static BOOL      (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags
 static void *    (WINAPI *pLocateXStateFeature)(CONTEXT *context, DWORD feature_id, DWORD *length);
 static BOOL      (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feature_mask);
 static BOOL      (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask);
+static BOOL      (WINAPI *pCopyContext)(CONTEXT *dst, DWORD context_flags, CONTEXT *src);
 
 #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
 
@@ -6849,6 +6851,365 @@ static void test_extended_context(void)
     for (i = 0; i < 8; ++i)
         ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
 }
+
+struct modified_range
+{
+    ULONG start;
+    ULONG flag;
+};
+
+#define check_changes_in_range(a, b, c, d) check_changes_in_range_(__FILE__, __LINE__, a, b, c, d)
+static void check_changes_in_range_(const char *file, unsigned int line, const BYTE *p,
+        const struct modified_range *range, ULONG flags, unsigned int length)
+{
+    ULONG range_flag, flag;
+    unsigned int once = 0;
+    unsigned int i;
+
+    range_flag = 0;
+    for (i = 0; i < length; i++)
+    {
+        if (i == range->start)
+        {
+            range_flag = range->flag;
+            ++range;
+        }
+
+        if ((flag = range_flag) == ~0)
+            continue;
+
+        if (flag & 0x80000000)
+        {
+            if (flag & flags && p[i] == 0xcc)
+            {
+                if (!once++)
+                    ok(broken(1), "Matched broken result at %#x, flags %#x.\n", i, flags);
+                continue;
+            }
+            flag = 0;
+        }
+
+        if (flag & flags && p[i] != 0xcc)
+        {
+            ok_(file, line)(0, "Got unexected byte %#x at %#x, flags %#x.\n", p[i], i, flags);
+            return;
+        }
+        else if (!(flag & flags) && p[i] != 0xdd)
+        {
+            ok_(file, line)(0, "Got unexected byte %#x at %#x, flags %#x.\n", p[i], i, flags);
+            return;
+        }
+    }
+    ok_(file, line)(1, "Range matches.\n");
+}
+
+static void test_copy_context(void)
+{
+    static const struct modified_range ranges_amd64[] =
+    {
+        {0x30, ~0}, {0x38, 0x1}, {0x3a, 0x4}, {0x42, 0x1}, {0x48, 0x10}, {0x78, 0x2}, {0x98, 0x1},
+        {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0x80000008}, {0x4b0, 0x10}, {0x4d0, ~0},
+        {0x4e8, 0}, {0x500, ~0}, {0x640, 0}, {0x1000, 0},
+    };
+    static const struct modified_range ranges_x86[] =
+    {
+        {0x0, ~0}, {0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0x80000020},
+        {0x2cc, ~0}, {0x294, 0}, {0x1000, 0},
+    };
+    static const struct modified_range single_range[] =
+    {
+        {0x0, 0x1}, {0x1000, 0},
+    };
+
+    static const struct
+    {
+        ULONG flags;
+    }
+    tests[] =
+    {
+        /* AMD64 */
+        {0x100000 | 0x01}, /* CONTEXT_CONTROL */
+        {0x100000 | 0x02}, /* CONTEXT_INTEGER */
+        {0x100000 | 0x04}, /* CONTEXT_SEGMENTS */
+        {0x100000 | 0x08}, /* CONTEXT_FLOATING_POINT */
+        {0x100000 | 0x10}, /* CONTEXT_DEBUG_REGISTERS */
+        {0x100000 | 0x0b}, /* CONTEXT_FULL */
+        {0x100000 | 0x40}, /* CONTEXT_XSTATE */
+        {0x100000 | 0x1f}, /* CONTEXT_ALL */
+        /* X86 */
+        { 0x10000 | 0x01}, /* CONTEXT_CONTROL */
+        { 0x10000 | 0x02}, /* CONTEXT_INTEGER */
+        { 0x10000 | 0x04}, /* CONTEXT_SEGMENTS */
+        { 0x10000 | 0x08}, /* CONTEXT_FLOATING_POINT */
+        { 0x10000 | 0x10}, /* CONTEXT_DEBUG_REGISTERS */
+        { 0x10000 | 0x20}, /* CONTEXT_EXTENDED_REGISTERS */
+        { 0x10000 | 0x40}, /* CONTEXT_XSTATE */
+        { 0x10000 | 0x3f}, /* CONTEXT_ALL */
+    };
+    static const ULONG arch_flags[] = {0x100000, 0x10000};
+
+    DECLSPEC_ALIGN(64) BYTE src_context_buffer[4096];
+    DECLSPEC_ALIGN(64) BYTE dst_context_buffer[4096];
+    ULONG64 enabled_features, expected_compaction;
+    unsigned int context_length, flags_offset, i;
+    CONTEXT_EX *src_ex, *dst_ex;
+    XSTATE *dst_xs, *src_xs;
+    BOOL compaction, bret;
+    CONTEXT *src, *dst;
+    NTSTATUS status;
+    DWORD length;
+    ULONG flags;
+
+    if (!pRtlCopyExtendedContext)
+    {
+        win_skip("RtlCopyExtendedContext is not available.\n");
+        return;
+    }
+
+    if (!pRtlGetEnabledExtendedFeatures)
+    {
+        skip("RtlGetEnabledExtendedFeatures is not available.\n");
+        return;
+    }
+
+    enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
+
+    for (i = 0; i < ARRAY_SIZE(tests); ++i)
+    {
+        flags = tests[i].flags;
+        flags_offset = (flags & 0x100000) ? 0x30 : 0;
+
+        memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
+        memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
+
+        status = pRtlInitializeExtendedContext(src_context_buffer, flags, &src_ex);
+        if (enabled_features || !(flags & 0x40))
+        {
+            ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        }
+        else
+        {
+            ok(status == STATUS_NOT_SUPPORTED, "Got unexpected status %#x, flags %#x.\n", status, flags);
+            continue;
+        }
+        status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+
+        src = pRtlLocateLegacyContext(src_ex, NULL);
+        dst = pRtlLocateLegacyContext(dst_ex, NULL);
+
+        *(DWORD *)((BYTE *)dst + flags_offset) = 0;
+        *(DWORD *)((BYTE *)src + flags_offset) = 0;
+
+        src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
+        memset(src_xs, 0xcc, sizeof(XSTATE));
+        src_xs->Mask = 3;
+        src_xs->CompactionMask = ~(ULONG64)0;
+
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+
+        context_length = (BYTE *)dst_ex - (BYTE *)dst + dst_ex->All.Length;
+        check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
+                flags, context_length);
+
+        ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                *(DWORD *)((BYTE *)dst + flags_offset), flags);
+
+        memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
+        status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        *(DWORD *)((BYTE *)src + flags_offset) = 0;
+        *(DWORD *)((BYTE *)dst + flags_offset) = 0;
+        SetLastError(0xdeadbeef);
+        bret = pCopyContext(dst, flags | 0x40, src);
+        ok((!bret && GetLastError() == (enabled_features ? ERROR_INVALID_PARAMETER : ERROR_NOT_SUPPORTED))
+                || broken(!bret && GetLastError() == ERROR_INVALID_PARAMETER),
+                "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
+                bret, GetLastError(), flags);
+        ok(*(DWORD *)((BYTE *)dst + flags_offset) == 0, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                *(DWORD *)((BYTE *)dst + flags_offset), flags);
+        check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
+                0, context_length);
+
+        *(DWORD *)((BYTE *)dst + flags_offset) = flags & 0x110000;
+        *(DWORD *)((BYTE *)src + flags_offset) = flags;
+        SetLastError(0xdeadbeef);
+        bret = pCopyContext(dst, flags, src);
+        if (flags & 0x40)
+            ok((!bret && GetLastError() == ERROR_MORE_DATA)
+                    || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
+                    "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
+                    bret, GetLastError(), flags);
+        else
+            ok((bret && GetLastError() == 0xdeadbeef)
+                    || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
+                    "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
+                    bret, GetLastError(), flags);
+        if (bret)
+        {
+            ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                    *(DWORD *)((BYTE *)dst + flags_offset), flags);
+            check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
+                    flags, context_length);
+        }
+        else
+        {
+            ok(*(DWORD *)((BYTE *)dst + flags_offset) == (flags & 0x110000),
+                    "Got unexpected ContextFlags %#x, flags %#x.\n",
+                    *(DWORD *)((BYTE *)dst + flags_offset), flags);
+            check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
+                    0, context_length);
+        }
+    }
+
+    for (i = 0; i < ARRAY_SIZE(arch_flags); ++i)
+    {
+        flags = arch_flags[i] | 0x42;
+        flags_offset = (flags & 0x100000) ? 0x30 : 0;
+        context_length = (flags & 0x100000) ? 0x4d0 : 0x2cc;
+
+        memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
+        memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
+        length = sizeof(src_context_buffer);
+        bret = pInitializeContext(src_context_buffer, flags, &src, &length);
+        ok(bret, "Got unexpected bret %#x, flags %#x.\n", bret, flags);
+
+        length = sizeof(dst_context_buffer);
+        bret = pInitializeContext(dst_context_buffer, flags, &dst, &length);
+        ok(bret, "Got unexpected bret %#x, flags %#x.\n", bret, flags);
+
+        dst_ex = (CONTEXT_EX *)((BYTE *)dst + context_length);
+        src_ex = (CONTEXT_EX *)((BYTE *)src + context_length);
+
+        dst_xs = (XSTATE *)((BYTE *)dst_ex + dst_ex->XState.Offset);
+        src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
+
+        *(DWORD *)((BYTE *)dst + flags_offset) = 0;
+        *(DWORD *)((BYTE *)src + flags_offset) = 0;
+
+        compaction = !!(src_xs->CompactionMask & ((ULONG64)1 << 63));
+        expected_compaction = (compaction ? ((ULONG64)1 << (ULONG64)63) | enabled_features : 0);
+
+        memset(&src_xs->YmmContext, 0xcc, sizeof(src_xs->YmmContext));
+        src_xs->CompactionMask = ~(ULONG64)0;
+
+        src_xs->Mask = 0;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        dst_ex->XState.Length = 0;
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(status == (enabled_features ? STATUS_BUFFER_OVERFLOW : STATUS_NOT_SUPPORTED),
+                "Got unexpected status %#x, flags %#x.\n", status, flags);
+
+        if (!enabled_features)
+            continue;
+
+        ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                *(DWORD *)((BYTE *)dst + flags_offset), flags);
+
+        src_xs->Mask = ~(ULONG64)0;
+
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        dst_ex->XState.Length = 0;
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(status == STATUS_BUFFER_OVERFLOW, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                *(DWORD *)((BYTE *)dst + flags_offset), flags);
+
+        ok(dst_xs->Mask == 0xdddddddddddddddd, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == 0xdddddddddddddddd, "Got unexpected CompactionMask %s.\n",
+                wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
+
+        src_xs->Mask = 3;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        dst_ex->XState.Length = offsetof(XSTATE, YmmContext);
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
+                *(DWORD *)((BYTE *)dst + flags_offset), flags);
+        ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == expected_compaction,
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
+
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        dst_ex->XState.Length = sizeof(XSTATE);
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == expected_compaction,
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
+
+        src_xs->Mask = 4;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == expected_compaction,
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
+
+        src_xs->Mask = 3;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == expected_compaction,
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
+
+
+        *(DWORD *)((BYTE *)src + flags_offset) = arch_flags[i];
+
+        src_xs->Mask = 7;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
+        ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
+        ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
+                wine_dbgstr_longlong(dst_xs->Mask));
+        ok(dst_xs->CompactionMask == expected_compaction,
+                "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
+
+        src_xs->Mask = 7;
+        memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
+        dst_xs->CompactionMask = 0xdddddddddddddddd;
+        dst_xs->Mask = 0xdddddddddddddddd;
+        SetLastError(0xdeadbeef);
+        bret = pCopyContext(dst, flags, src);
+        ok((bret && GetLastError() == 0xdeadbeef)
+                || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
+                "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
+                bret, GetLastError(), flags);
+        ok(dst_xs->Mask == 0xdddddddddddddddd || broken(dst_xs->Mask == 4), "Got unexpected Mask %s, flags %#x.\n",
+                wine_dbgstr_longlong(dst_xs->Mask), flags);
+        ok(dst_xs->CompactionMask == 0xdddddddddddddddd || broken(dst_xs->CompactionMask == expected_compaction),
+                "Got unexpected CompactionMask %s, flags %#x.\n", wine_dbgstr_longlong(dst_xs->CompactionMask), flags);
+        check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range,
+                dst_xs->Mask == 4, sizeof(dst_xs->YmmContext));
+    }
+}
 #endif
 
 START_TEST(exception)
@@ -6903,6 +7264,7 @@ START_TEST(exception)
     X(RtlLocateLegacyContext);
     X(RtlSetExtendedFeaturesMask);
     X(RtlGetExtendedFeaturesMask);
+    X(RtlCopyExtendedContext);
 #undef X
 
 #define X(f) p##f = (void*)GetProcAddress(hkernel32, #f)
@@ -6914,6 +7276,7 @@ START_TEST(exception)
     X(LocateXStateFeature);
     X(SetXStateFeaturesMask);
     X(GetXStateFeaturesMask);
+    X(CopyContext);
 #undef X
 
     if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
@@ -6994,6 +7357,7 @@ START_TEST(exception)
     test_prot_fault();
     test_kiuserexceptiondispatcher();
     test_extended_context();
+    test_copy_context();
 
 #elif defined(__x86_64__)
 
@@ -7033,6 +7397,7 @@ START_TEST(exception)
     else
       skip( "Dynamic unwind functions not found\n" );
     test_extended_context();
+    test_copy_context();
 
 #elif defined(__aarch64__)
 
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index ed09ffcd924..8892fde5054 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -1010,6 +1010,7 @@
 @ stdcall -arch=win32 -ret64 RtlConvertLongToLargeInteger(long)
 @ stdcall RtlConvertSidToUnicodeString(ptr ptr long)
 @ stdcall -arch=win32 -ret64 RtlConvertUlongToLargeInteger(long)
+@ stdcall RtlCopyExtendedContext(ptr long ptr)
 @ stdcall RtlCopyLuid(ptr ptr)
 @ stdcall RtlCopyLuidAndAttributesArray(long ptr ptr)
 @ stdcall -arch=x86_64 RtlCopyMemory(ptr ptr long)
diff --git a/include/ddk/wdm.h b/include/ddk/wdm.h
index 60f926d7c5a..6885de1cf89 100644
--- a/include/ddk/wdm.h
+++ b/include/ddk/wdm.h
@@ -1837,6 +1837,7 @@ BOOLEAN   WINAPI PsGetVersion(ULONG*,ULONG*,ULONG*,UNICODE_STRING*);
 NTSTATUS  WINAPI PsTerminateSystemThread(NTSTATUS);
 
 #if defined(__x86_64__) || defined(__i386__)
+NTSTATUS  WINAPI RtlCopyExtendedContext(CONTEXT_EX*,ULONG,CONTEXT_EX*);
 NTSTATUS  WINAPI RtlInitializeExtendedContext(void*,ULONG,CONTEXT_EX**);
 NTSTATUS  WINAPI RtlInitializeExtendedContext2(void*,ULONG,CONTEXT_EX**,ULONG64);
 ULONG64   WINAPI RtlGetEnabledExtendedFeatures(ULONG64);
diff --git a/include/winbase.h b/include/winbase.h
index 1c29db367c2..ab3155eb1e1 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1827,6 +1827,7 @@ WINBASEAPI BOOL        WINAPI CommConfigDialogW(LPCWSTR,HWND,LPCOMMCONFIG);
 WINBASEAPI BOOL        WINAPI ConnectNamedPipe(HANDLE,LPOVERLAPPED);
 WINBASEAPI BOOL        WINAPI ContinueDebugEvent(DWORD,DWORD,DWORD);
 WINBASEAPI HANDLE      WINAPI ConvertToGlobalHandle(HANDLE hSrc);
+WINBASEAPI BOOL        WINAPI CopyContext(CONTEXT*, DWORD, CONTEXT*);
 WINBASEAPI BOOL        WINAPI CopyFileA(LPCSTR,LPCSTR,BOOL);
 WINBASEAPI BOOL        WINAPI CopyFileW(LPCWSTR,LPCWSTR,BOOL);
 #define                       CopyFile WINELIB_NAME_AW(CopyFile)
-- 
2.26.2




More information about the wine-devel mailing list