Blame src/thread/ntapi_tt_create_thread.c

dd89bb
/********************************************************/
dd89bb
/*  ntapi: Native API core library                      */
64e606
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
dd89bb
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
dd89bb
/********************************************************/
dd89bb
dd89bb
#include <psxtypes/psxtypes.h>
dd89bb
#include <pemagine/pemagine.h>
dd89bb
#include <ntapi/nt_status.h>
dd89bb
#include <ntapi/nt_object.h>
dd89bb
#include <ntapi/nt_memory.h>
dd89bb
#include <ntapi/nt_thread.h>
dd89bb
#include <ntapi/nt_process.h>
dd89bb
#include <ntapi/nt_string.h>
80236a
#include <ntapi/nt_atomic.h>
dd89bb
#include <ntapi/ntapi.h>
dd89bb
#include "ntapi_impl.h"
dd89bb
dd89bb
/* (no planned support of alpha processors, use constant values) */
dd89bb
#define __PAGE_SIZE 		0x001000
dd89bb
#define __GRANULARITY		0x010000
dd89bb
#define __RESERVE_ROUND_UP	0x100000
dd89bb
e870a2
static int32_t __create_thread_fail(
dd89bb
	void *	hprocess,
dd89bb
	void *	stack_bottom,
dd89bb
	size_t	stack_size,
dd89bb
	int32_t status)
dd89bb
{
dd89bb
	__ntapi->zw_free_virtual_memory(
dd89bb
		hprocess,
dd89bb
		&stack_bottom,
dd89bb
		&stack_size,
dd89bb
		NT_MEM_RELEASE);
dd89bb
	return status;
dd89bb
}
dd89bb
dd89bb
2db6fe
static int32_t __tt_create_thread_stack(
2db6fe
	nt_thread_params *	params,
2db6fe
	nt_user_stack *		stack)
2db6fe
{
2db6fe
	int32_t		status;
2db6fe
	uint32_t	protect_type_old;
2db6fe
	void *		stack_system_limit;
2db6fe
	char *		base;
2db6fe
	size_t		size;
2db6fe
	size_t		commit;
2db6fe
2db6fe
	/* caller-provided stack? */
2db6fe
	if (params->stack_info && params->stack_info->expandable_stack_base) {
2db6fe
		stack->fixed_stack_base        = params->stack_info->fixed_stack_base;
2db6fe
		stack->fixed_stack_limit       = params->stack_info->fixed_stack_limit;
2db6fe
		stack->expandable_stack_base   = params->stack_info->expandable_stack_base;
2db6fe
		stack->expandable_stack_limit  = params->stack_info->expandable_stack_limit;
2db6fe
		stack->expandable_stack_bottom = params->stack_info->expandable_stack_bottom;
2db6fe
2db6fe
		return NT_STATUS_SUCCESS;
2db6fe
	}
dd89bb
dd89bb
	/* init */
dd89bb
	params->stack_size_commit  = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit+params->ext_ctx_size, __PAGE_SIZE);
dd89bb
	params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_reserve,__GRANULARITY);
dd89bb
dd89bb
	/* compare, round-up as needed */
dd89bb
	if (params->stack_size_commit >= params->stack_size_reserve)
dd89bb
		params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit,__RESERVE_ROUND_UP);
dd89bb
dd89bb
	/**
dd89bb
	  *
dd89bb
	  *  --------- BASE ----------
dd89bb
	  *
dd89bb
	  *  ---- (COMMITED AREA) ----
dd89bb
	  *
dd89bb
	  *  --------- LIMIT ---------
dd89bb
	  *
dd89bb
	  *  ------ GUARD PAGE -------
dd89bb
	  *
dd89bb
	  *  ------ ACTUAL LIMIT -----
dd89bb
	  *
dd89bb
	  *  ---- (RESERVED AREA) ----
dd89bb
	  *
dd89bb
	  *  -------- BOTTOM ---------
dd89bb
	  *
dd89bb
	**/
dd89bb
dd89bb
	/* stack structure: unused fields */
2db6fe
	stack->fixed_stack_base  = 0;
2db6fe
	stack->fixed_stack_limit = 0;
dd89bb
dd89bb
	/* first we reserve */
2db6fe
	stack->expandable_stack_bottom = 0;
dd89bb
e870a2
	if ((status = __ntapi->zw_allocate_virtual_memory(
e870a2
			params->hprocess,
2db6fe
			&stack->expandable_stack_bottom,
e870a2
			params->stack_zero_bits,
e870a2
			&params->stack_size_reserve,
e870a2
			NT_MEM_RESERVE,
e870a2
			NT_PAGE_READWRITE)))
e870a2
		return status;
dd89bb
dd89bb
	/* calculate base and limit */
2db6fe
	base  = stack->expandable_stack_bottom;
e870a2
	base += params->stack_size_reserve;
dd89bb
2db6fe
	stack->expandable_stack_base   = base;
2db6fe
	stack->expandable_stack_limit  = base - params->stack_size_commit;
dd89bb
dd89bb
	/* guard page */
e870a2
	commit             = params->stack_size_commit + __PAGE_SIZE;
e870a2
	stack_system_limit = base - commit;
dd89bb
dd89bb
	/* then we commit */
e870a2
	if ((status = __ntapi->zw_allocate_virtual_memory(
e870a2
			params->hprocess,
e870a2
			&stack_system_limit,
e870a2
			0,&commit,
e870a2
			NT_MEM_COMMIT,
e870a2
			NT_PAGE_READWRITE)))
e870a2
		return __create_thread_fail(
e870a2
			params->hprocess,
2db6fe
			stack->expandable_stack_bottom,
e870a2
			params->stack_size_reserve,
e870a2
			status);
dd89bb
dd89bb
	/* finally we protect the guard page */
656c60
	size = __PAGE_SIZE;
e870a2
e870a2
	if ((status = __ntapi->zw_protect_virtual_memory(
e870a2
			params->hprocess,
e870a2
			&stack_system_limit,
e870a2
			&size,
e870a2
			NT_PAGE_READWRITE | NT_MEM_PAGE_GUARD,
e870a2
			&protect_type_old)))
e870a2
		return __create_thread_fail(
e870a2
			params->hprocess,
2db6fe
			stack->expandable_stack_bottom,
e870a2
			params->stack_size_reserve,
e870a2
			status);
dd89bb
2db6fe
	/* all done */
2db6fe
	if (params->stack_info) {
2db6fe
		params->stack_info->fixed_stack_base        = stack->fixed_stack_base;
2db6fe
		params->stack_info->fixed_stack_limit       = stack->fixed_stack_limit;
2db6fe
		params->stack_info->expandable_stack_base   = stack->expandable_stack_base;
2db6fe
		params->stack_info->expandable_stack_limit  = stack->expandable_stack_limit;
2db6fe
		params->stack_info->expandable_stack_bottom = stack->expandable_stack_bottom;
2db6fe
2db6fe
		return NT_STATUS_SUCCESS;
2db6fe
	}
2db6fe
2db6fe
	return NT_STATUS_SUCCESS;
2db6fe
}
2db6fe
2db6fe
int32_t __stdcall __ntapi_tt_create_thread(nt_thread_params * params)
2db6fe
{
2db6fe
	int32_t				status;
2db6fe
	ntapi_internals *		__internals;
2db6fe
2db6fe
	nt_client_id			cid;
2db6fe
	nt_port_message_csrss_process	csrss_msg;
2db6fe
	nt_port_message_csrss_process *	csrss_msg_1st;
2db6fe
	nt_port_message_csrss_thread *	csrss_msg_any;
2db6fe
2db6fe
	nt_thread_context 	__attr_aligned__(0x40) context;
2db6fe
	nt_user_stack		__attr_aligned__(0x10) stack;
2db6fe
	uintptr_t		fsuspended;
2db6fe
	uintptr_t *		parg;
2db6fe
2db6fe
	if (!(params->stack_size_commit))
2db6fe
		return NT_STATUS_INVALID_PARAMETER;
2db6fe
	else if (!(params->stack_size_reserve))
2db6fe
		return NT_STATUS_INVALID_PARAMETER;
2db6fe
	else if (params->ext_ctx_size > __NT_INTERNAL_PAGE_SIZE)
2db6fe
		return NT_STATUS_INVALID_PARAMETER;
2db6fe
	else if (params->ext_ctx_size % sizeof(intptr_t))
2db6fe
		return NT_STATUS_INVALID_PARAMETER;
2db6fe
	else if (params->arg && params->ext_ctx)
2db6fe
		return NT_STATUS_INVALID_PARAMETER_MIX;
2db6fe
	else if (params->ext_ctx && !params->ext_ctx_size)
2db6fe
		return NT_STATUS_INVALID_PARAMETER_MIX;
2db6fe
2db6fe
	/* init */
2db6fe
	__internals  = __ntapi_internals();
2db6fe
2db6fe
	/* stack */
2db6fe
	if ((status = __tt_create_thread_stack(params,&stack)))
2db6fe
		return status;
2db6fe
dd89bb
	/* context */
3bc37f
	if (params->reg_context) {
3bc37f
		__ntapi->tt_aligned_block_memcpy(
3bc37f
			(uintptr_t *)&context,
3bc37f
			(uintptr_t *)params->reg_context,
3bc37f
			sizeof(context));
3bc37f
	} else {
3bc37f
		__ntapi->tt_aligned_block_memset(
3bc37f
			&context,0,sizeof(context));
3bc37f
dd89bb
		__INIT_CONTEXT(context);
dd89bb
		context.INSTRUCTION_POINTER_REGISTER = (uintptr_t)params->start;
360317
		context.STACK_POINTER_REGISTER       = (uintptr_t)(stack.expandable_stack_base);
dd89bb
	}
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
/*****************************************************************************/
dd89bb
/*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/
dd89bb
/*                                                                           */
dd89bb
/*                                                                           */
dd89bb
/*      INNOVATION IN THE FIELD OF MULTI-THREADED COMPUTER PROGRAMMING       */
dd89bb
/*                                                                           */
dd89bb
/*      A "RAPUNZEL" TOP-OF-STACK, VARIABLE-SIZE ENTRY-ROUTINE CONTEXT       */
dd89bb
/*                                                                           */
64e606
/*                   COPYRIGHT (C) 2013--2021 SysDeer Technologies, LLC      */
dd89bb
/*                                                                           */
dd89bb
/*                                                                           */
dd89bb
/*                                                                           */
dd89bb
/*      Laß mir dein Haar herunter.«                                         */
dd89bb
/**/	if (params->ext_ctx) {                                             /**/
dd89bb
/**/		context.STACK_POINTER_REGISTER -= params->ext_ctx_size;    /**/
dd89bb
/**/		params->arg = (void *)context.STACK_POINTER_REGISTER;      /**/
dd89bb
/**/                                                                       /**/
80236a
/**/		if (params->creation_flags & NT_CREATE_LOCAL_THREAD) {     /**/
dd89bb
/**/			__ntapi->tt_aligned_block_memcpy(                  /**/
dd89bb
/**/				(uintptr_t *)params->arg,                  /**/
dd89bb
/**/				(uintptr_t *)params->ext_ctx,              /**/
dd89bb
/**/				params->ext_ctx_size);                     /**/
80236a
/**/                                                                       /**/
80236a
/**/			__ntapi->tt_aligned_block_memlock(                 /**/
80236a
/**/				(uintptr_t *)params->arg,                  /**/
80236a
/**/				params->ext_ctx_size);                     /**/
80236a
/**/                                                                       /**/
80236a
/**/		} else {                                                   /**/
dd89bb
/**/			status = __ntapi->zw_write_virtual_memory(         /**/
dd89bb
/**/				params->hprocess,                          /**/
dd89bb
/**/				params->arg,                               /**/
dd89bb
/**/				(char *)params->ext_ctx,                   /**/
dd89bb
/**/				params->ext_ctx_size,                      /**/
dd89bb
/**/				0);                                        /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/			if (status) return __create_thread_fail(           /**/
dd89bb
/**/				params->hprocess,                          /**/
dd89bb
/**/				stack.expandable_stack_bottom,             /**/
dd89bb
/**/				params->stack_size_reserve,                /**/
dd89bb
/**/				status);                                   /**/
dd89bb
/**/		}                                                          /**/
dd89bb
/**/	}                                                                  /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/                                                                       /**/
dd89bb
/*      entry-routine argument address and stack pointer adjustment          */
dd89bb
/**/	if (sizeof(intptr_t) == 4) {                                       /**/
dd89bb
/**/		context.STACK_POINTER_REGISTER -= sizeof(intptr_t);        /**/
dd89bb
/**/		parg  = (uintptr_t *)context.STACK_POINTER_REGISTER;       /**/
c2fe99
/**/	} else {                                                           /**/
dd89bb
/**/		parg  = &context.FAST_CALL_ARG0;                           /**/
c2fe99
/**/    }                                                                  /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/                                                                       /**/
dd89bb
/*      write entry-routine argument                                         */
d7c519
/**/	if (sizeof(intptr_t) == 8) {                                       /**/
80236a
/**/		at_store(                                                  /**/
80236a
/**/			(intptr_t *)parg,                                  /**/
80236a
/**/			(intptr_t)params->arg);                            /**/
d7c519
/**/                                                                       /**/
d7c519
/**/	} else if (params->creation_flags & NT_CREATE_LOCAL_THREAD) {      /**/
d7c519
/**/		at_store(                                                  /**/
d7c519
/**/			(intptr_t *)parg,                                  /**/
d7c519
/**/			(intptr_t)params->arg);                            /**/
d7c519
/**/                                                                       /**/
d7c519
/**/	} else {                                                           /**/
dd89bb
/**/		status = __ntapi->zw_write_virtual_memory(                 /**/
dd89bb
/**/			params->hprocess,                                  /**/
dd89bb
/**/			parg,                                              /**/
dd89bb
/**/			(char *)&params->arg,                              /**/
dd89bb
/**/			sizeof(uintptr_t),                                 /**/
dd89bb
/**/			0);                                                /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/		if (status) return __create_thread_fail(                   /**/
dd89bb
/**/			params->hprocess,                                  /**/
dd89bb
/**/			stack.expandable_stack_bottom,                     /**/
dd89bb
/**/			params->stack_size_reserve,                        /**/
dd89bb
/**/			status);                                           /**/
dd89bb
/**/	}                                                                  /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/                                                                       /**/
dd89bb
/*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/
dd89bb
/*****************************************************************************/
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
dd89bb
	/* create thread */
e870a2
	fsuspended = (params->creation_flags & NT_CREATE_SUSPENDED)
e870a2
		? 1 : __ntapi->zw_create_user_process
e870a2
			? 0 : 1;
e870a2
e870a2
	if ((status = __ntapi->zw_create_thread(
e870a2
			&params->hthread,
e870a2
			NT_THREAD_ALL_ACCESS,
e870a2
			params->obj_attr,
e870a2
			params->hprocess,
e870a2
			&cid,&context,&stack,
e870a2
			fsuspended)))
e870a2
		return __create_thread_fail(
e870a2
			params->hprocess,
e870a2
			stack.expandable_stack_bottom,
e870a2
			params->stack_size_reserve,
e870a2
			status);
dd89bb
dd89bb
	/* for os versions prior to hasta la */
dd89bb
	if (!__ntapi->zw_create_user_process) {
dd89bb
		__ntapi->tt_aligned_block_memset(&csrss_msg,0,sizeof(csrss_msg));
dd89bb
dd89bb
		if (params->creation_flags & NT_CREATE_FIRST_THREAD_OF_PROCESS) {
dd89bb
			/* nt_port_message_csrss_process is the larger structure */
dd89bb
			csrss_msg_1st = &csrss_msg;
dd89bb
dd89bb
			csrss_msg_1st->header.data_size		= sizeof(nt_port_message_csrss_process) - sizeof(nt_port_message);
dd89bb
			csrss_msg_1st->header.msg_size		= sizeof(nt_port_message_csrss_process);
dd89bb
			csrss_msg_1st->opcode			= 0x10000;
dd89bb
			csrss_msg_1st->hprocess			= params->hprocess;
dd89bb
			csrss_msg_1st->hthread			= params->hthread;
dd89bb
			csrss_msg_1st->unique_process_id	= cid.process_id;
dd89bb
			csrss_msg_1st->unique_thread_id		= cid.thread_id;
dd89bb
		} else {
dd89bb
			/* nt_port_message_csrss_thread is the smaller structure */
dd89bb
			csrss_msg_any = (nt_port_message_csrss_thread *)&csrss_msg;
dd89bb
dd89bb
			csrss_msg_any->header.data_size		= sizeof(nt_port_message_csrss_thread) - sizeof(nt_port_message);
dd89bb
			csrss_msg_any->header.msg_size		= sizeof(nt_port_message_csrss_thread);
dd89bb
			csrss_msg_any->opcode			= 0x10001;
dd89bb
			csrss_msg_any->hthread			= params->hthread;
dd89bb
			csrss_msg_any->unique_process_id	= cid.process_id;
dd89bb
			csrss_msg_any->unique_thread_id		= cid.thread_id;
dd89bb
		}
dd89bb
dd89bb
		/* send csrss a new-thread notification */
dd89bb
		if (__internals->csr_port_handle_addr) {
dd89bb
			status = __ntapi->zw_request_wait_reply_port(
dd89bb
				*__internals->csr_port_handle_addr,
dd89bb
				&csrss_msg,&csrss_msg);
dd89bb
		}
dd89bb
dd89bb
		/* output csrss_status to caller */
dd89bb
		params->csrss_status = status
dd89bb
			? status
dd89bb
			: csrss_msg.status;
dd89bb
	}
dd89bb
dd89bb
	/* resume thread, close handle as needed */
dd89bb
	if (fsuspended && !(params->creation_flags & NT_CREATE_SUSPENDED))
dd89bb
		status = __ntapi->zw_resume_thread(params->hthread,0);
dd89bb
dd89bb
	if (params->creation_flags & NT_CLOSE_THREAD_HANDLE)
dd89bb
		__ntapi->zw_close(params->hthread);
dd89bb
dd89bb
	/* and finally */
c8a126
	params->cid.process_id = cid.process_id;
c8a126
	params->cid.thread_id  = cid.thread_id;
e870a2
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}
dd89bb
dd89bb
e870a2
int32_t __stdcall __ntapi_tt_create_local_thread(nt_thread_params * params)
dd89bb
{
e870a2
	nt_status			status;
dd89bb
	void *				image_base;
dd89bb
	struct pe_stack_heap_info	stack_heap_info;
dd89bb
f36e2d
	/* flags */
f36e2d
	params->creation_flags |= NT_CREATE_LOCAL_THREAD;
f36e2d
f36e2d
	/* hprocess */
f36e2d
	if (!params->hprocess)
f36e2d
		params->hprocess = __ntapi_internals()->hprocess;
f36e2d
f36e2d
	/* stack defaults */
e870a2
	if (params->stack_info)
e870a2
		(void)0;
dd89bb
e870a2
	else if (params->stack_size_commit && params->stack_size_reserve)
e870a2
		(void)0;
dd89bb
e870a2
	else {
e870a2
		/* image_base*/
e870a2
		if (!(image_base = pe_get_first_module_handle()))
e870a2
			return NT_STATUS_INVALID_IMPORT_OF_NON_DLL;
dd89bb
e870a2
		if ((status = pe_get_image_stack_heap_info(
e870a2
				image_base,
e870a2
				&stack_heap_info)))
dd89bb
			return NT_STATUS_INVALID_IMAGE_FORMAT;
dd89bb
dd89bb
		/* stack_size_commit */
dd89bb
		if (!params->stack_size_commit)
dd89bb
			params->stack_size_commit = stack_heap_info.size_of_stack_commit;
dd89bb
dd89bb
		/* stack_size_reserve */
dd89bb
		if (!params->stack_size_reserve)
dd89bb
			params->stack_size_reserve = stack_heap_info.size_of_stack_reserve;
dd89bb
dd89bb
		if (!(params->stack_size_commit && params->stack_size_reserve))
dd89bb
			return NT_STATUS_INVALID_IMAGE_FORMAT;
dd89bb
	}
dd89bb
e870a2
	return __ntapi_tt_create_thread(params);
dd89bb
}
dd89bb
dd89bb
e870a2
int32_t __stdcall __ntapi_tt_create_remote_thread(nt_thread_params * params)
dd89bb
{
dd89bb
	return __ntapi_tt_create_thread(params);
dd89bb
}
dd89bb
dd89bb
dd89bb
void * __cdecl __ntapi_csr_port_handle(nt_status * pstatus)
dd89bb
{
e870a2
	nt_status		status;
e870a2
	ntapi_internals *	__internals;
dd89bb
e870a2
	pstatus     = pstatus ? pstatus : &status;
dd89bb
	__internals = __ntapi_internals();
dd89bb
dd89bb
	if (__internals->csr_port_handle_addr) {
e870a2
		*pstatus = NT_STATUS_SUCCESS;
dd89bb
		return *__internals->csr_port_handle_addr;
dd89bb
	} else {
e870a2
		*pstatus = NT_STATUS_UNSUCCESSFUL;
e870a2
		return 0;
dd89bb
	}
dd89bb
}