Blame src/thread/ntapi_tt_create_thread.c

dd89bb
/********************************************************/
dd89bb
/*  ntapi: Native API core library                      */
4256e2
/*  Copyright (C) 2013--2016  Z. Gilboa                 */
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>
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
dd89bb
static int32_t __stdcall __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
int32_t __stdcall __ntapi_tt_create_thread(
dd89bb
	__in_out	nt_thread_params *	params)
dd89bb
{
dd89bb
	int32_t				status;
dd89bb
	ntapi_internals *		__internals;
dd89bb
dd89bb
	nt_client_id			cid;
dd89bb
	nt_port_message_csrss_process	csrss_msg;
dd89bb
	nt_port_message_csrss_process *	csrss_msg_1st;
dd89bb
	nt_port_message_csrss_thread *	csrss_msg_any;
dd89bb
dd89bb
	void *			stack_system_limit;
dd89bb
	uint32_t		protect_type_old;
dd89bb
9f88a2
	nt_thread_context 	context __attr_aligned__(0x40);
9f88a2
	nt_user_stack		stack   __attr_aligned__(0x10);
dd89bb
	uintptr_t		fsuspended;
dd89bb
	uintptr_t *		parg;
656c60
	size_t			size;
656c60
	size_t			commit;
dd89bb
dd89bb
	if (!(params->stack_size_commit))
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
	else if (!(params->stack_size_reserve))
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
	else if (params->ext_ctx_size > __NT_INTERNAL_PAGE_SIZE)
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
	else if (params->ext_ctx_size % sizeof(intptr_t))
dd89bb
		return NT_STATUS_INVALID_PARAMETER;
dd89bb
	else if (params->arg && params->ext_ctx)
dd89bb
		return NT_STATUS_INVALID_PARAMETER_MIX;
dd89bb
	else if (params->ext_ctx && !params->ext_ctx_size)
dd89bb
		return NT_STATUS_INVALID_PARAMETER_MIX;
dd89bb
dd89bb
	/* init */
dd89bb
	__internals  = __ntapi_internals();
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 */
dd89bb
	stack.fixed_stack_base  = (void *)0;
dd89bb
	stack.fixed_stack_limit = (void *)0;
dd89bb
dd89bb
	/* first we reserve */
dd89bb
	stack.expandable_stack_bottom = (void *)0;
dd89bb
	status = __ntapi->zw_allocate_virtual_memory(
dd89bb
		params->hprocess,
dd89bb
		&stack.expandable_stack_bottom,
dd89bb
		params->stack_zero_bits,
dd89bb
		&params->stack_size_reserve,
dd89bb
		NT_MEM_RESERVE,
dd89bb
		NT_PAGE_READWRITE);
dd89bb
dd89bb
	if (status) return status;
dd89bb
dd89bb
	/* calculate base and limit */
dd89bb
	stack.expandable_stack_base =
dd89bb
		(void *)((intptr_t)stack.expandable_stack_bottom
dd89bb
			+ params->stack_size_reserve);
dd89bb
dd89bb
	stack.expandable_stack_limit =
dd89bb
		(void *)((intptr_t)stack.expandable_stack_base
dd89bb
			- params->stack_size_commit);
dd89bb
dd89bb
	/* guard page */
656c60
	commit = params->stack_size_commit + __PAGE_SIZE;
dd89bb
	stack_system_limit =
dd89bb
		(void *)((intptr_t)stack.expandable_stack_base
656c60
			- commit);
dd89bb
dd89bb
	/* then we commit */
dd89bb
	status = __ntapi->zw_allocate_virtual_memory(
dd89bb
		params->hprocess,
dd89bb
		&stack_system_limit,
dd89bb
		0,
656c60
		&commit,
dd89bb
		NT_MEM_COMMIT,
dd89bb
		NT_PAGE_READWRITE);
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
	/* finally we protect the guard page */
656c60
	size = __PAGE_SIZE;
dd89bb
	status = __ntapi->zw_protect_virtual_memory(
dd89bb
		params->hprocess,
dd89bb
		&stack_system_limit,
656c60
		&size,
dd89bb
		NT_PAGE_READWRITE | NT_MEM_PAGE_GUARD,
dd89bb
		&protect_type_old);
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
	/* context */
dd89bb
	if (!params->reg_context) {
dd89bb
		params->reg_context = &context;
dd89bb
		__ntapi->tt_aligned_block_memset(&context,0,sizeof(nt_thread_context));
dd89bb
		__INIT_CONTEXT(context);
dd89bb
		context.INSTRUCTION_POINTER_REGISTER = (uintptr_t)params->start;
dd89bb
		context.STACK_POINTER_REGISTER = (uintptr_t)(stack.expandable_stack_base)
dd89bb
						- sizeof(intptr_t);
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
/*                                                                           */
dd89bb
/*                   COPYRIGHT (C) 2013,2014,2015 ZVI GILBOA                 */
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
/**/                                                                       /**/
dd89bb
/**/		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);                     /**/
dd89bb
/**/		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;       /**/
dd89bb
/**/	} else                                                             /**/
dd89bb
/**/		parg  = &context.FAST_CALL_ARG0;                           /**/
dd89bb
/**/                                                                       /**/
dd89bb
/**/                                                                       /**/
dd89bb
/*      write entry-routine argument                                         */
dd89bb
/**/	if ((sizeof(size_t) == 8)                                          /**/
dd89bb
/**/			|| (params->creation_flags&NT_CREATE_LOCAL_THREAD))/**/
dd89bb
/**/		*parg = (uintptr_t)params->arg;                            /**/
dd89bb
/**/	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 */
dd89bb
	if ((!__ntapi->zw_create_user_process) | (params->creation_flags & NT_CREATE_SUSPENDED))
dd89bb
		fsuspended = 1;
dd89bb
	else
dd89bb
		fsuspended = 0;
dd89bb
dd89bb
	status = __ntapi->zw_create_thread(
dd89bb
		&params->hthread,
dd89bb
		NT_THREAD_ALL_ACCESS,
dd89bb
		params->obj_attr,
dd89bb
		params->hprocess,
dd89bb
		&cid,
dd89bb
		params->reg_context,
dd89bb
		&stack,
dd89bb
		fsuspended);
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
	/* 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 */
dd89bb
	params->thread_id = (uint32_t)cid.thread_id;
dd89bb
	return NT_STATUS_SUCCESS;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_create_local_thread(
dd89bb
	__in_out	nt_thread_params *	params)
dd89bb
{
dd89bb
	void *				image_base;
dd89bb
	struct pe_stack_heap_info	stack_heap_info;
dd89bb
	nt_client_id			cid;
dd89bb
	nt_object_attributes		oa;
dd89bb
	nt_status			status;
dd89bb
dd89bb
        /* oa init */
dd89bb
	oa.len		= sizeof(oa);
dd89bb
	oa.root_dir	= (void *)0;
dd89bb
	oa.obj_name	= (nt_unicode_string *)0;
dd89bb
	oa.obj_attr	= 0;
dd89bb
	oa.sec_desc	= (nt_sd *)0;
dd89bb
	oa.sec_qos	= (nt_sqos *)0;
dd89bb
dd89bb
        /* init cid */
dd89bb
        cid.process_id = pe_get_current_process_id();
dd89bb
        cid.thread_id  = pe_get_current_thread_id();
dd89bb
dd89bb
	/* obtain a handle to our own process */
dd89bb
	/* TODO: use cached handle, no close  */
dd89bb
	status = __ntapi->zw_open_process(
dd89bb
		&params->hprocess,
dd89bb
		NT_PROCESS_ALL_ACCESS,
dd89bb
		&oa,
dd89bb
		&cid;;
dd89bb
dd89bb
	if (status) return status;
dd89bb
dd89bb
	/* retrieve the stack defaults as needed */
dd89bb
	if (!(params->stack_size_commit && params->stack_size_reserve) && !(params->stack_info)) {
dd89bb
		/* image_base*/
dd89bb
		image_base = pe_get_first_module_handle();
dd89bb
dd89bb
		if (!(intptr_t)image_base)
dd89bb
			return NT_STATUS_INVALID_IMPORT_OF_NON_DLL;
dd89bb
dd89bb
		status = pe_get_image_stack_heap_info(
dd89bb
			image_base,
dd89bb
			&stack_heap_info);
dd89bb
dd89bb
		if (status)
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
dd89bb
	params->creation_flags |= NT_CREATE_LOCAL_THREAD;
dd89bb
	status =  __ntapi_tt_create_thread(params);
dd89bb
dd89bb
	/* TODO: use cached handle, no close  */
dd89bb
	__ntapi->zw_close(params->hprocess);
dd89bb
	return status;
dd89bb
}
dd89bb
dd89bb
dd89bb
int32_t __stdcall __ntapi_tt_create_remote_thread(
dd89bb
	__in_out	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
{
dd89bb
	ntapi_internals * __internals;
dd89bb
dd89bb
	__internals = __ntapi_internals();
dd89bb
dd89bb
	if (__internals->csr_port_handle_addr) {
dd89bb
		if (pstatus)
dd89bb
			*pstatus = NT_STATUS_SUCCESS;
dd89bb
		return *__internals->csr_port_handle_addr;
dd89bb
	} else {
dd89bb
		if (pstatus)
dd89bb
			*pstatus = NT_STATUS_UNSUCCESSFUL;
dd89bb
		return (void *)0;
dd89bb
	}
dd89bb
}