Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <pemagine/pemagine.h>
#include <ntapi/nt_status.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_memory.h>
#include <ntapi/nt_thread.h>
#include <ntapi/nt_process.h>
#include <ntapi/nt_string.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"

/* (no planned support of alpha processors, use constant values) */
#define __PAGE_SIZE 		0x001000
#define __GRANULARITY		0x010000
#define __RESERVE_ROUND_UP	0x100000

static int32_t __create_thread_fail(
	void *	hprocess,
	void *	stack_bottom,
	size_t	stack_size,
	int32_t status)
{
	__ntapi->zw_free_virtual_memory(
		hprocess,
		&stack_bottom,
		&stack_size,
		NT_MEM_RELEASE);
	return status;
}


static int32_t __tt_create_thread_stack(
	nt_thread_params *	params,
	nt_user_stack *		stack)
{
	int32_t		status;
	uint32_t	protect_type_old;
	void *		stack_system_limit;
	char *		base;
	size_t		size;
	size_t		commit;

	/* caller-provided stack? */
	if (params->stack_info && params->stack_info->expandable_stack_base) {
		stack->fixed_stack_base        = params->stack_info->fixed_stack_base;
		stack->fixed_stack_limit       = params->stack_info->fixed_stack_limit;
		stack->expandable_stack_base   = params->stack_info->expandable_stack_base;
		stack->expandable_stack_limit  = params->stack_info->expandable_stack_limit;
		stack->expandable_stack_bottom = params->stack_info->expandable_stack_bottom;

		return NT_STATUS_SUCCESS;
	}

	/* init */
	params->stack_size_commit  = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit+params->ext_ctx_size, __PAGE_SIZE);
	params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_reserve,__GRANULARITY);

	/* compare, round-up as needed */
	if (params->stack_size_commit >= params->stack_size_reserve)
		params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit,__RESERVE_ROUND_UP);

	/**
	  *
	  *  --------- BASE ----------
	  *
	  *  ---- (COMMITED AREA) ----
	  *
	  *  --------- LIMIT ---------
	  *
	  *  ------ GUARD PAGE -------
	  *
	  *  ------ ACTUAL LIMIT -----
	  *
	  *  ---- (RESERVED AREA) ----
	  *
	  *  -------- BOTTOM ---------
	  *
	**/

	/* stack structure: unused fields */
	stack->fixed_stack_base  = 0;
	stack->fixed_stack_limit = 0;

	/* first we reserve */
	stack->expandable_stack_bottom = 0;

	if ((status = __ntapi->zw_allocate_virtual_memory(
			params->hprocess,
			&stack->expandable_stack_bottom,
			params->stack_zero_bits,
			&params->stack_size_reserve,
			NT_MEM_RESERVE,
			NT_PAGE_READWRITE)))
		return status;

	/* calculate base and limit */
	base  = stack->expandable_stack_bottom;
	base += params->stack_size_reserve;

	stack->expandable_stack_base   = base;
	stack->expandable_stack_limit  = base - params->stack_size_commit;

	/* guard page */
	commit             = params->stack_size_commit + __PAGE_SIZE;
	stack_system_limit = base - commit;

	/* then we commit */
	if ((status = __ntapi->zw_allocate_virtual_memory(
			params->hprocess,
			&stack_system_limit,
			0,&commit,
			NT_MEM_COMMIT,
			NT_PAGE_READWRITE)))
		return __create_thread_fail(
			params->hprocess,
			stack->expandable_stack_bottom,
			params->stack_size_reserve,
			status);

	/* finally we protect the guard page */
	size = __PAGE_SIZE;

	if ((status = __ntapi->zw_protect_virtual_memory(
			params->hprocess,
			&stack_system_limit,
			&size,
			NT_PAGE_READWRITE | NT_MEM_PAGE_GUARD,
			&protect_type_old)))
		return __create_thread_fail(
			params->hprocess,
			stack->expandable_stack_bottom,
			params->stack_size_reserve,
			status);

	/* all done */
	if (params->stack_info) {
		params->stack_info->fixed_stack_base        = stack->fixed_stack_base;
		params->stack_info->fixed_stack_limit       = stack->fixed_stack_limit;
		params->stack_info->expandable_stack_base   = stack->expandable_stack_base;
		params->stack_info->expandable_stack_limit  = stack->expandable_stack_limit;
		params->stack_info->expandable_stack_bottom = stack->expandable_stack_bottom;

		return NT_STATUS_SUCCESS;
	}

	return NT_STATUS_SUCCESS;
}

int32_t __stdcall __ntapi_tt_create_thread(nt_thread_params * params)
{
	int32_t				status;
	ntapi_internals *		__internals;

	nt_client_id			cid;
	nt_port_message_csrss_process	csrss_msg;
	nt_port_message_csrss_process *	csrss_msg_1st;
	nt_port_message_csrss_thread *	csrss_msg_any;

	nt_thread_context 	__attr_aligned__(0x40) context;
	nt_user_stack		__attr_aligned__(0x10) stack;
	uintptr_t		fsuspended;
	uintptr_t *		parg;

	if (!(params->stack_size_commit))
		return NT_STATUS_INVALID_PARAMETER;
	else if (!(params->stack_size_reserve))
		return NT_STATUS_INVALID_PARAMETER;
	else if (params->ext_ctx_size > __NT_INTERNAL_PAGE_SIZE)
		return NT_STATUS_INVALID_PARAMETER;
	else if (params->ext_ctx_size % sizeof(intptr_t))
		return NT_STATUS_INVALID_PARAMETER;
	else if (params->arg && params->ext_ctx)
		return NT_STATUS_INVALID_PARAMETER_MIX;
	else if (params->ext_ctx && !params->ext_ctx_size)
		return NT_STATUS_INVALID_PARAMETER_MIX;

	/* init */
	__internals  = __ntapi_internals();

	/* stack */
	if ((status = __tt_create_thread_stack(params,&stack)))
		return status;

	/* context */
	if (params->reg_context) {
		__ntapi->tt_aligned_block_memcpy(
			(uintptr_t *)&context,
			(uintptr_t *)params->reg_context,
			sizeof(context));
	} else {
		__ntapi->tt_aligned_block_memset(
			&context,0,sizeof(context));

		__INIT_CONTEXT(context);
		context.INSTRUCTION_POINTER_REGISTER = (uintptr_t)params->start;
		context.STACK_POINTER_REGISTER       = (uintptr_t)(stack.expandable_stack_base);
	}







/*****************************************************************************/
/*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/
/*                                                                           */
/*                                                                           */
/*      INNOVATION IN THE FIELD OF MULTI-THREADED COMPUTER PROGRAMMING       */
/*                                                                           */
/*      A "RAPUNZEL" TOP-OF-STACK, VARIABLE-SIZE ENTRY-ROUTINE CONTEXT       */
/*                                                                           */
/*                   COPYRIGHT (C) 2013--2021 SysDeer Technologies, LLC      */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/*      Laß mir dein Haar herunter.«                                         */
/**/	if (params->ext_ctx) {                                             /**/
/**/		context.STACK_POINTER_REGISTER -= params->ext_ctx_size;    /**/
/**/		params->arg = (void *)context.STACK_POINTER_REGISTER;      /**/
/**/                                                                       /**/
/**/		if (params->creation_flags & NT_CREATE_LOCAL_THREAD) {     /**/
/**/			__ntapi->tt_aligned_block_memcpy(                  /**/
/**/				(uintptr_t *)params->arg,                  /**/
/**/				(uintptr_t *)params->ext_ctx,              /**/
/**/				params->ext_ctx_size);                     /**/
/**/                                                                       /**/
/**/			__ntapi->tt_aligned_block_memlock(                 /**/
/**/				(uintptr_t *)params->arg,                  /**/
/**/				params->ext_ctx_size);                     /**/
/**/                                                                       /**/
/**/		} else {                                                   /**/
/**/			status = __ntapi->zw_write_virtual_memory(         /**/
/**/				params->hprocess,                          /**/
/**/				params->arg,                               /**/
/**/				(char *)params->ext_ctx,                   /**/
/**/				params->ext_ctx_size,                      /**/
/**/				0);                                        /**/
/**/                                                                       /**/
/**/			if (status) return __create_thread_fail(           /**/
/**/				params->hprocess,                          /**/
/**/				stack.expandable_stack_bottom,             /**/
/**/				params->stack_size_reserve,                /**/
/**/				status);                                   /**/
/**/		}                                                          /**/
/**/	}                                                                  /**/
/**/                                                                       /**/
/**/                                                                       /**/
/**/                                                                       /**/
/*      entry-routine argument address and stack pointer adjustment          */
/**/	if (sizeof(intptr_t) == 4) {                                       /**/
/**/		context.STACK_POINTER_REGISTER -= sizeof(intptr_t);        /**/
/**/		parg  = (uintptr_t *)context.STACK_POINTER_REGISTER;       /**/
/**/	} else {                                                           /**/
/**/		parg  = &context.FAST_CALL_ARG0;                           /**/
/**/    }                                                                  /**/
/**/                                                                       /**/
/**/                                                                       /**/
/*      write entry-routine argument                                         */
/**/	if (sizeof(intptr_t) == 8) {                                       /**/
/**/		at_store(                                                  /**/
/**/			(intptr_t *)parg,                                  /**/
/**/			(intptr_t)params->arg);                            /**/
/**/                                                                       /**/
/**/	} else if (params->creation_flags & NT_CREATE_LOCAL_THREAD) {      /**/
/**/		at_store(                                                  /**/
/**/			(intptr_t *)parg,                                  /**/
/**/			(intptr_t)params->arg);                            /**/
/**/                                                                       /**/
/**/	} else {                                                           /**/
/**/		status = __ntapi->zw_write_virtual_memory(                 /**/
/**/			params->hprocess,                                  /**/
/**/			parg,                                              /**/
/**/			(char *)&params->arg,                              /**/
/**/			sizeof(uintptr_t),                                 /**/
/**/			0);                                                /**/
/**/                                                                       /**/
/**/		if (status) return __create_thread_fail(                   /**/
/**/			params->hprocess,                                  /**/
/**/			stack.expandable_stack_bottom,                     /**/
/**/			params->stack_size_reserve,                        /**/
/**/			status);                                           /**/
/**/	}                                                                  /**/
/**/                                                                       /**/
/**/                                                                       /**/
/*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/
/*****************************************************************************/











	/* create thread */
	fsuspended = (params->creation_flags & NT_CREATE_SUSPENDED)
		? 1 : __ntapi->zw_create_user_process
			? 0 : 1;

	if ((status = __ntapi->zw_create_thread(
			&params->hthread,
			NT_THREAD_ALL_ACCESS,
			params->obj_attr,
			params->hprocess,
			&cid,&context,&stack,
			fsuspended)))
		return __create_thread_fail(
			params->hprocess,
			stack.expandable_stack_bottom,
			params->stack_size_reserve,
			status);

	/* for os versions prior to hasta la */
	if (!__ntapi->zw_create_user_process) {
		__ntapi->tt_aligned_block_memset(&csrss_msg,0,sizeof(csrss_msg));

		if (params->creation_flags & NT_CREATE_FIRST_THREAD_OF_PROCESS) {
			/* nt_port_message_csrss_process is the larger structure */
			csrss_msg_1st = &csrss_msg;

			csrss_msg_1st->header.data_size		= sizeof(nt_port_message_csrss_process) - sizeof(nt_port_message);
			csrss_msg_1st->header.msg_size		= sizeof(nt_port_message_csrss_process);
			csrss_msg_1st->opcode			= 0x10000;
			csrss_msg_1st->hprocess			= params->hprocess;
			csrss_msg_1st->hthread			= params->hthread;
			csrss_msg_1st->unique_process_id	= cid.process_id;
			csrss_msg_1st->unique_thread_id		= cid.thread_id;
		} else {
			/* nt_port_message_csrss_thread is the smaller structure */
			csrss_msg_any = (nt_port_message_csrss_thread *)&csrss_msg;

			csrss_msg_any->header.data_size		= sizeof(nt_port_message_csrss_thread) - sizeof(nt_port_message);
			csrss_msg_any->header.msg_size		= sizeof(nt_port_message_csrss_thread);
			csrss_msg_any->opcode			= 0x10001;
			csrss_msg_any->hthread			= params->hthread;
			csrss_msg_any->unique_process_id	= cid.process_id;
			csrss_msg_any->unique_thread_id		= cid.thread_id;
		}

		/* send csrss a new-thread notification */
		if (__internals->csr_port_handle_addr) {
			status = __ntapi->zw_request_wait_reply_port(
				*__internals->csr_port_handle_addr,
				&csrss_msg,&csrss_msg);
		}

		/* output csrss_status to caller */
		params->csrss_status = status
			? status
			: csrss_msg.status;
	}

	/* resume thread, close handle as needed */
	if (fsuspended && !(params->creation_flags & NT_CREATE_SUSPENDED))
		status = __ntapi->zw_resume_thread(params->hthread,0);

	if (params->creation_flags & NT_CLOSE_THREAD_HANDLE)
		__ntapi->zw_close(params->hthread);

	/* and finally */
	params->cid.process_id = cid.process_id;
	params->cid.thread_id  = cid.thread_id;

	return NT_STATUS_SUCCESS;
}


int32_t __stdcall __ntapi_tt_create_local_thread(nt_thread_params * params)
{
	nt_status			status;
	void *				image_base;
	struct pe_stack_heap_info	stack_heap_info;

	/* flags */
	params->creation_flags |= NT_CREATE_LOCAL_THREAD;

	/* hprocess */
	if (!params->hprocess)
		params->hprocess = __ntapi_internals()->hprocess;

	/* stack defaults */
	if (params->stack_info)
		(void)0;

	else if (params->stack_size_commit && params->stack_size_reserve)
		(void)0;

	else {
		/* image_base*/
		if (!(image_base = pe_get_first_module_handle()))
			return NT_STATUS_INVALID_IMPORT_OF_NON_DLL;

		if ((status = pe_get_image_stack_heap_info(
				image_base,
				&stack_heap_info)))
			return NT_STATUS_INVALID_IMAGE_FORMAT;

		/* stack_size_commit */
		if (!params->stack_size_commit)
			params->stack_size_commit = stack_heap_info.size_of_stack_commit;

		/* stack_size_reserve */
		if (!params->stack_size_reserve)
			params->stack_size_reserve = stack_heap_info.size_of_stack_reserve;

		if (!(params->stack_size_commit && params->stack_size_reserve))
			return NT_STATUS_INVALID_IMAGE_FORMAT;
	}

	return __ntapi_tt_create_thread(params);
}


int32_t __stdcall __ntapi_tt_create_remote_thread(nt_thread_params * params)
{
	return __ntapi_tt_create_thread(params);
}


void * __cdecl __ntapi_csr_port_handle(nt_status * pstatus)
{
	nt_status		status;
	ntapi_internals *	__internals;

	pstatus     = pstatus ? pstatus : &status;
	__internals = __ntapi_internals();

	if (__internals->csr_port_handle_addr) {
		*pstatus = NT_STATUS_SUCCESS;
		return *__internals->csr_port_handle_addr;
	} else {
		*pstatus = NT_STATUS_UNSUCCESSFUL;
		return 0;
	}
}