Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] The ultimate ros amd64 bringup #361

Closed
wants to merge 54 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
91c8699
[NTOS:IO] Fix parsing of resource lists
tkreuzer Jan 28, 2018
ff1eaad
[NTOS:MM] Make sure to call MmInitializeProcessAddressSpace() from th…
tkreuzer Jan 29, 2018
15a08e8
[NTOS:MM] In the x64 version of MmCreateProcessAddressSpace() zero ou…
tkreuzer Jan 29, 2018
cce3d16
[NTOS:MM] Fix ViewSize parameter passed to MiInsertVadEx() from MiCre…
tkreuzer Jan 29, 2018
286b2fb
[NTOS:MM] On x64 reserve the address range between FFFF800000000000 a…
tkreuzer Jan 29, 2018
12002d4
[HAL] Fix some 64 bit warnings
tkreuzer Jan 29, 2018
546c05b
[NTOS:OB] Fix some 64 bit warnings
tkreuzer Jan 29, 2018
1bc15af
[CRT] Use chkstk_ms.s on x64 builds
tkreuzer Jan 29, 2018
1f6d61b
[CRT] Remove the x64 asm version of sqrt from build
tkreuzer Jan 29, 2018
9e097de
[NTOS:IO] Fix a warning on MSVC builds
tkreuzer Jan 29, 2018
aedb9f6
[NTOS:MM] SLIST handling for kernel stacks
tkreuzer Feb 4, 2018
d139743
[NTOS:MM] Handle PPEs und PXEs as well in MmInitializeProcessAddressS…
tkreuzer Feb 4, 2018
25064d0
[NTOS:MM] Simplify and fix x64 version of MiGetPteForProcess(), fix M…
tkreuzer Feb 4, 2018
75e11df
[NTOS:MM] Fix session space initialization on x64
tkreuzer Feb 4, 2018
13ef07a
[NTOS:MM] Fix paged pool initialization on x64
tkreuzer Feb 4, 2018
03920b1
[NTOS:MM] Fix paged pool expansion
tkreuzer Feb 4, 2018
235566d
[NTOS:SE] Fix SeSetSecurityDescriptorInfoEx to avoid pool corruption …
tkreuzer Feb 4, 2018
1e326fb
[CMAKE] Add baseaddress_msvc_x64.cmake
tkreuzer Feb 4, 2018
c4fbc15
[CSRSRV] Fix type of ViewSize parameter passed to NtMapViewOfSection
tkreuzer Feb 4, 2018
29db059
[NTOS:KE] Implement KiConvertToGuiThread, KeSwitchKernelStack and sup…
tkreuzer Feb 4, 2018
c24203c
[NTOS:KE] Save and restore previous mode in KiZwSystemService and fix…
tkreuzer Feb 5, 2018
c9d1f6a
[NTOS:KE] Implement KiGetUserModeStackAddress() and KeUserModeCallback()
tkreuzer Feb 5, 2018
e503f53
[HAL][NTOS][I8042PRT] Add some hacks related to resource conflicts
tkreuzer Feb 5, 2018
9335f8f
[NTOS:MM] HACK: comment out an ASSERT that still fails on x64
tkreuzer Feb 5, 2018
4b4a334
[REACTOS] Fix many 64 bit warnings
tkreuzer Feb 6, 2018
945d807
[ENVIRON] Fix x64 build
tkreuzer Feb 6, 2018
a26ae1d
[BOOTLIB] Fix 64 bit warnings
tkreuzer Feb 6, 2018
bd078ab
[NTOS:KE] Improve kernel stack switching on GUI system calls
tkreuzer Feb 6, 2018
7749bdc
Fix indentation
tkreuzer Feb 6, 2018
177b3e7
Addendum to session space stuff
tkreuzer Feb 7, 2018
52721c2
Remove a trailing white space
tkreuzer Feb 7, 2018
fbec870
[NDK] Update x64 version of KEXCEPTION_FRAME
tkreuzer Feb 9, 2018
d5bdd7e
[NTOS:KE] Change the logic of KeSwitchKernelStack and friends to be s…
tkreuzer Feb 9, 2018
4351118
[ASM] Add initial version of kxamd64.inc
tkreuzer Feb 9, 2018
5b1588c
[NDK] Add UCALLOUT_FRAME definition
tkreuzer Feb 10, 2018
1dc22b6
[NTOS:KE] Move KiInitializeUserApc to usercall.c
tkreuzer Feb 10, 2018
8366b2c
[NTOS:KE] Simplify KiInitializeUserApc
tkreuzer Feb 10, 2018
29d20df
[NTOS:KE] Implement x64 version of user callback code
tkreuzer Feb 10, 2018
10e086b
[NDK] Fix the type of KPROCESS::ActiveProcessors
tkreuzer Feb 10, 2018
3252ac3
Silence annoying UNIMPLEMENTED messages
tkreuzer Feb 10, 2018
66eb02c
[NTOS:MM] Remove obsolete x64 debug print
tkreuzer Feb 10, 2018
c93d924
[NTOS:PS] Fix an issue with PROCESS_DEVICEMAP_INFORMATION size on 64…
tkreuzer Feb 10, 2018
c3380ef
[NTDLL] Delete obsolete file amd64/stubs.c
tkreuzer Feb 11, 2018
c249c15
[NTOS:PS] On x64 don't fail in NtSetInformationProcess with ProcessUs…
tkreuzer Feb 11, 2018
8b48097
[WIN32K] Fix ULONG/SIZE_T issue
tkreuzer Feb 11, 2018
e7e7e40
[NTOS:KE] Silence KiRundownThread, it has nothing to do
tkreuzer Feb 11, 2018
a9fc91d
[VIDEOPRT] Don't try to use NtVdmControl on x64
tkreuzer Feb 11, 2018
6e0a3be
[VIDEOPRT] [FORMATTING] No code change
tkreuzer Feb 11, 2018
f062e2d
[VIDEOPRT] Call IntInt10CallBios from VideoPortInt10, instead of code…
tkreuzer Feb 11, 2018
21177bb
[VIDEOPRT] Fixes for x64
tkreuzer Feb 11, 2018
ebe9bc4
[NDK] Add BIOS call API for amd64
tkreuzer Feb 11, 2018
c8bd63f
[FAST486] DWORD -> ULONG, so it can be used in kernel mode
tkreuzer Feb 11, 2018
4c80d83
[HAL] Implement amd64 BIOS call support
tkreuzer Feb 11, 2018
96f3020
[VIDEOPRT] Implement support for INT10 on x64 using the newly impleme…
tkreuzer Feb 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 96 additions & 6 deletions ntoskrnl/ke/amd64/stubs.c
Expand Up @@ -8,6 +8,7 @@
/* INCLUDES ******************************************************************/

#include <ntoskrnl.h>
#include <fltkernel.h>

#define NDEBUG
#include <debug.h>
Expand All @@ -17,6 +18,10 @@ KiRetireDpcListInDpcStack(
PKPRCB Prcb,
PVOID DpcStack);

NTSTATUS
KiConvertToGuiThread(
VOID);

VOID
NTAPI
KiDpcInterruptHandler(VOID)
Expand Down Expand Up @@ -86,13 +91,66 @@ KeZeroPages(IN PVOID Address,
RtlZeroMemory(Address, Size);
}

PVOID
KiSwitchKernelStackHelper(
LONG_PTR StackOffset,
PVOID OldStackBase);

PVOID
NTAPI
KeSwitchKernelStack(PVOID StackBase, PVOID StackLimit)
{
UNIMPLEMENTED;
__debugbreak();
return NULL;
PKTHREAD CurrentThread;
PVOID OldStackBase;
LONG_PTR StackOffset;
SIZE_T StackSize;

/* Get the current thread */
CurrentThread = KeGetCurrentThread();

/* Save the old stack base */
OldStackBase = CurrentThread->StackBase;

/* Get the size of the current stack */
StackSize = (ULONG_PTR)CurrentThread->StackBase - CurrentThread->StackLimit;
ASSERT(StackSize <= (ULONG_PTR)StackBase - (ULONG_PTR)StackLimit);

/* Copy the current stack contents to the new stack */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add a note that this can be optimized by only copying what's above (rsp-32) or some such?

RtlCopyMemory((PUCHAR)StackBase - StackSize,
(PVOID)CurrentThread->StackLimit,
StackSize);

/* Calculate the offset between the old and the new stack */
StackOffset = (PUCHAR)StackBase - (PUCHAR)CurrentThread->StackBase;

/* Disable interrupts while messing with the stack */
_disable();

/* Set the new trap frame */
CurrentThread->TrapFrame = (PKTRAP_FRAME)Add2Ptr(CurrentThread->TrapFrame,
StackOffset);

/* Set the new initial stack */
CurrentThread->InitialStack = Add2Ptr(CurrentThread->InitialStack,
StackOffset);

/* Set the new stack limits */
CurrentThread->StackBase = StackBase;
CurrentThread->StackLimit = (ULONG_PTR)StackLimit;
CurrentThread->LargeStack = TRUE;

/* Adjust the PCR fields */
__addgsqword(FIELD_OFFSET(KPCR, NtTib.StackBase), StackOffset);
__addgsqword(FIELD_OFFSET(KIPCR, Prcb.RspBase), StackOffset);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fail to see how, except for those two lines, this function is specific to amd64. Can't we factor out a general x86 version ?


/* Finally switch RSP to the new stack.
We pass OldStackBase to make sure it is not lost. */
OldStackBase = KiSwitchKernelStackHelper(StackOffset, OldStackBase);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certain compilers with certain options may use rbp as a frame pointer, which your code wouldn't translate.
For that matter, the x86 version translates ebp but not frame pointers (or any variables) that are already saved on stack. How does that work reliably?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and if we are honest, we can't do it this way on x86 either, unless all the code that leads to this is entirely written in assembly. For x64 I have a solution that is "by the book", i.e. it returns to asm code, then switches the stack and repeats the system call handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the code so that it should fix the issue. This is x64 only (because there the logic of returning to the "root" is already there, while on x86 we still rely on "good faith" that the compiler doesn't do totally awkward stuff.


/* Reenable interrupts */
_enable();

return OldStackBase;
}

NTSTATUS
Expand Down Expand Up @@ -315,9 +373,7 @@ KiSystemCallHandler(
PULONG64 KernelParams, UserParams;
ULONG ServiceNumber, Offset, Count;
ULONG64 UserRsp;

DPRINT("Syscall #%ld\n", TrapFrame->Rax);
//__debugbreak();
NTSTATUS Status;

/* Increase system call count */
__addgsdword(FIELD_OFFSET(KIPCR, Prcb.KeSystemCalls), 1);
Expand Down Expand Up @@ -354,6 +410,40 @@ KiSystemCallHandler(
/* Get descriptor table */
DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset);

/* Validate the system call number */
if (ServiceNumber >= DescriptorTable->Limit)
{
/* Check if this is a GUI call */
if (!(Offset & SERVICE_TABLE_TEST))
{
/* Fail the call */
TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
return (PVOID)NtSyscallFailure;
}

/* Convert us to a GUI thread -- must wrap in ASM to get new EBP */
Status = KiConvertToGuiThread();

/* Reload trap frame and descriptor table pointer from new stack */
TrapFrame = *(volatile PVOID*)&Thread->TrapFrame;
DescriptorTable = (PVOID)(*(volatile ULONG_PTR*)&Thread->ServiceTable + Offset);

if (!NT_SUCCESS(Status))
{
/* Set the last error and fail */
TrapFrame->Rax = Status;
return (PVOID)NtSyscallFailure;
}

/* Validate the system call number again */
if (ServiceNumber >= DescriptorTable->Limit)
{
/* Fail the call */
TrapFrame->Rax = STATUS_INVALID_SYSTEM_SERVICE;
return (PVOID)NtSyscallFailure;
}
}

/* Get stack bytes and calculate argument count */
Count = DescriptorTable->Number[ServiceNumber] / 8;

Expand Down
45 changes: 45 additions & 0 deletions ntoskrnl/ke/amd64/trap.S
Expand Up @@ -21,6 +21,7 @@ EXTERN KiGeneralProtectionFaultHandler:PROC
EXTERN KiXmmExceptionHandler:PROC
EXTERN KiDeliverApc:PROC
EXTERN KiDpcInterruptHandler:PROC
EXTERN PsConvertToGuiThread:PROC

#ifdef _WINKD_
EXTERN KdSetOwedBreakpoints:PROC
Expand Down Expand Up @@ -966,6 +967,28 @@ FUNC KiZwSystemService

ENDFUNC

PUBLIC KiConvertToGuiThread
FUNC KiConvertToGuiThread

push rbp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, here's something I just learned... x64 functions must not begin with a single-byte instruction (/hotpatch is always implicitly on, and no NOP is used). PSDK's shared/macamd64.inc has a rex_push_reg macro that explains this (somewhat). MS uses a redundant 0x48 REX prefix to ensure this.
I know we have lots of code failing this requirement, but we should probably follow it going forward.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably create that include file and start using it. Seems like the right way to make things portable (eventually) and avoid mistakes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x64 functions must not begin with a single-byte instruction [...]

And what's the technical/philosophical reason behind this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I answered that in parentheses right after the part you quoted. Hotpatching needs a two byte instruction (otherwise a running instance of the function might be in the middle of the first, then try to read the second instruction which has been overwritten with the second half of the patched-in jmp). x64 doesn't use "mov edi, edi", it mandates instead that functions must start with a two-byte (or larger) instruction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh year, hotpatching KiConvertToGuiThread is totally important! What could happen, if we didn't support that? Guess what: On Windows it starts with a 1 byte intruction, too! And KiInterruptTemplate also! I say: let all internal functions start with 1 byte, to make patching unreliable for nasty hackers, since that are the only people who would do that.

.pushreg rbp
sub rsp, 32
.allocstack 32
.endprolog

/* Call the worker function */
call PsConvertToGuiThread

/* Adjust rsp */
add rsp, 32

/* Restore rbp */
pop rbp

/* return to the caller */
ret

ENDFUNC

KiExitToUserApc:
int 3
Expand Down Expand Up @@ -1008,6 +1031,28 @@ KiInitializeSegments:
mov gs, ax
ret

/*!
* VOID
* KiSwitchKernelStackHelper(
* LONG_PTR StackOffset,
* PVOID OldStackBase);
*/
PUBLIC KiSwitchKernelStackHelper
KiSwitchKernelStackHelper:

/* Pop return address from the current stack */
pop rax

/* Switch to new stack */
lea rsp, [rsp + rcx]

/* Push return address on the new stack */
push rax

/* Return on new stack */
mov rax, rdx
ret;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superfluous ;



#ifdef _MSC_VER
#undef lgdt
Expand Down