/*
 * \brief  Implementation of Thread API interface for core
 * \author Norman Feske
 * \date   2006-05-03
 */

/*
 * Copyright (C) 2006-2012 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

/* Codezero includes */
#include <codezero/syscalls.h>

/* Genode includes */
#include <base/thread.h>
#include <base/printf.h>
#include <base/sleep.h>

/* core includes */
#include <platform.h>
#include <platform_thread.h>

enum { verbose_thread_start = true };

using namespace Genode;


void Thread_base::_init_platform_thread() { }


void Thread_base::_deinit_platform_thread() { }


/**
 * Create and start new thread
 *
 * \param space_no   space ID in which the new thread will be executed
 * \param sp         initial stack pointer
 * \param ip         initial instruction pointer
 * \return           new thread ID, or
 *                   negative error code
 */
inline int create_thread(int space_no,
                         void *sp, void *ip,
                         int pager_tid = 1)
{
	using namespace Codezero;

	struct task_ids ids = { 1, space_no, TASK_ID_INVALID };

	/* allocate new thread at the kernel */
	unsigned long flags = THREAD_CREATE | TC_SHARE_SPACE | TC_SHARE_GROUP;
	int ret = l4_thread_control(flags, &ids);
	if (ret < 0) {
		PERR("l4_thread_control returned %d, spid=%d\n",
		     ret, ids.spid);
		return -1;
	}

	unsigned long utcb_base_addr = (unsigned long)l4_get_utcb();

	/* calculate utcb address of new thread */
	unsigned long new_utcb = utcb_base_addr + ids.tid*sizeof(struct utcb);

	/* setup thread context */
	struct exregs_data exregs;
	exregs_set_stack(&exregs, (unsigned long)sp);
	exregs_set_pc   (&exregs, (unsigned long)ip);
	exregs_set_pager(&exregs, pager_tid);
	exregs_set_utcb (&exregs, new_utcb);

	ret = l4_exchange_registers(&exregs, ids.tid);
	if (ret < 0) {
		printf("l4_exchange_registers returned ret=%d\n", ret);
		return -2;
	}

	/* start execution */
	ret = l4_thread_control(THREAD_RUN, &ids);
	if (ret < 0) {
		printf("Error: l4_thread_control(THREAD_RUN) returned %d\n", ret);
		return -3;
	}

	/* return new thread ID allocated by the kernel */
	return ids.tid;
}


void Thread_base::_thread_start()
{
	Thread_base::myself()->_thread_bootstrap();
	Thread_base::myself()->entry();
	sleep_forever();
}


void Thread_base::start()
{
	/* create and start platform thread */
	_tid.pt = new(platform()->core_mem_alloc()) Platform_thread(_context->name);

	_tid.l4id = create_thread(1, &_context->stack[-4], (void *)&_thread_start);
	if (_tid.l4id.tid < 0)
		PERR("create_thread returned %d", _tid.l4id.tid);

	if (verbose_thread_start)
		printf("core started local thread \"%s\" with ID %d\n",
		       _context->name, _tid.l4id.tid);
}


void Thread_base::cancel_blocking()
{
	PWRN("not implemented");
}


