/*
 * \brief  Kernel initialization
 * \author Martin Stein
 * \date   2010-07-22
 */

/*
 * Copyright (C) 2010-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.
 */

#include <platform/platform.h>
#include <generic/scheduler.h>
#include "include/thread.h"
#include <generic/printf.h>
#include <generic/verbose.h>
#include <generic/blocking.h>
#include <kernel/config.h>
#include <kernel/syscalls.h>
#include <cpu/prints.h>
#include <util/array.h>

using namespace Cpu;
using namespace Kernel;

enum { KERNEL__VERBOSE = 0,
       KERNEL__WARNING = 1,
       KERNEL__ERROR   = 1 };


/* Can be defined via compiler options */
extern "C" void ROOTTASK_ENTRY();

extern Kernel::Exec_context* _userland_context;
extern Utcb* _main_utcb_addr;

extern int _exit_kernel;


Kernel::Thread_factory* Kernel::thread_factory()
{
	static Thread_factory _tf;
	return &_tf;
}


namespace Kernel {

	static Utcb _roottask_utcb, _idle_utcb;

	void _roottask_thread__verbose__creation(addr_t vip, addr_t vsp, Utcb* vutcb)
	{
		if (!KERNEL__VERBOSE) return;
		printf("Kernel::roottask_thread, roottask thread created, "
		       "printing constraints\n");
		printf("  vip=0x%8X, vsp=0x%8X, vutcb=0x%8X\n",
		       (uint32_t)vip, (uint32_t)vsp, (uint32_t)vutcb);
	}


	void idle()
	{
		while(1);
	}


	Thread *idle_thread()
	{
		enum{
			IDLE_STACK_WORD_SIZE=32,
			IDLE_TID=1,
		};

		static word_t    _it_stack[IDLE_STACK_WORD_SIZE];
		static Thread *_it = thread_factory()->get(IDLE_TID);

		if (!_it) {

			Thread_factory::Create_argument itca;

			itca.tid           = (Thread_id)IDLE_TID;
			itca.pid           = (Protection_id)Roottask::PROTECTION_ID;
			itca.utcb          = &_idle_utcb;
			itca.pager_tid     = INVALID_THREAD_ID;
			itca.vsp           = (addr_t)&LAST_ARRAY_ELEM(_it_stack);
			itca.vip           = (addr_t)&idle;
			itca.is_privileged = true;

			_it = thread_factory()->create(&itca, true);
		}
		return _it;
	}


	Thread *roottask_thread()
	{
		static word_t  _rt_stack[Roottask::MAIN_STACK_SIZE/WORD_SIZE];
		static Thread *_rt = thread_factory()->get(Roottask::MAIN_THREAD_ID);

		if (!_rt) {

			Thread_factory::Create_argument rtca;

			rtca.tid           = (Thread_id)Roottask::MAIN_THREAD_ID;
			rtca.pid           = (Protection_id)Roottask::PROTECTION_ID;
			rtca.utcb          = &_roottask_utcb;
			rtca.pager_tid     = INVALID_THREAD_ID;
			rtca.vsp           = (addr_t)&LAST_ARRAY_ELEM(_rt_stack);
			rtca.vip           = (addr_t)&ROOTTASK_ENTRY;
			rtca.is_privileged = true;
			_main_utcb_addr    = rtca.utcb;

			_rt = thread_factory()->create(&rtca, false);
			if (_rt)
				_roottask_thread__verbose__creation(
					rtca.vip, rtca.vsp, rtca.utcb);
		}
		return _rt;
	}
}


Platform *Kernel::platform() { static Platform _p; return &_p; }


Scheduler *Kernel::scheduler()
{
	static bool _init_scheduler = false;
	static Scheduler _s = Scheduler(platform(),
		                            platform()->timer(),
		                            idle_thread());
	if(_init_scheduler){ return &_s; }
	_s.add(roottask_thread());
	_init_scheduler = true;
	return &_s;
}


Tlb *Kernel::tlb() { return platform()->tlb(); }


Irq_controller * const Kernel::irq_controller()
{
	return platform()->irq_controller();
}


Irq_allocator * const Kernel::irq_allocator()
{
	static Irq_allocator _ia =
		Irq_allocator(platform()->irq_controller());
	return &_ia;
}


unsigned Kernel::word_width() { return Platform::WORD_WIDTH; }


void Kernel::halt() { platform()->halt(); }


Kernel::Kernel_entry *Kernel::kernel_entry_event()
{
	static Kernel_entry _ke;
	return &_ke;
}


Kernel::Kernel_exit *Kernel::kernel_exit_event()
{
	static Kernel_exit _kx;
	return &_kx;
}


/**
 * Kernel main routine, gets called by crt0_kernel.s
 */
extern "C" void _kernel()
{
	kernel_entry_event()->on_occurence();

	scheduler()->run();

	kernel_exit_event()->on_occurence();
}

