genode/base-hw/src/core/kernel/kernel.cc

288 lines
7.3 KiB
C++

/*
* \brief Singlethreaded minimalistic kernel
* \author Martin Stein
* \date 2011-10-20
*
* This kernel is the only code except the mode transition PIC, that runs in
* privileged CPU mode. It has two tasks. First it initializes the process
* 'core', enriches it with the whole identically mapped address range,
* joins and applies it, assigns one thread to it with a userdefined
* entrypoint (the core main thread) and starts this thread in userland.
* Afterwards it is called each time an exception occurs in userland to do
* a minimum of appropriate exception handling. Thus it holds a CPU context
* for itself as for any other thread. But due to the fact that it never
* relies on prior kernel runs this context only holds some constant pointers
* such as SP and IP.
*/
/*
* Copyright (C) 2011-2013 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.
*/
/* core includes */
#include <kernel/pd.h>
#include <kernel/vm.h>
#include <kernel/irq.h>
#include <platform_pd.h>
#include <trustzone.h>
#include <timer.h>
#include <pic.h>
/* base includes */
#include <unmanaged_singleton.h>
/* base-hw includes */
#include <kernel/perf_counter.h>
using namespace Kernel;
/* get core configuration */
extern Genode::Native_utcb * _main_thread_utcb;
extern Genode::Native_thread_id _main_thread_id;
extern int _kernel_stack_high;
extern "C" void CORE_MAIN();
namespace Kernel
{
/**
* Return interrupt-controller singleton
*/
Pic * pic() { return unmanaged_singleton<Pic>(); }
/* import Genode types */
typedef Genode::umword_t umword_t;
typedef Genode::Core_tlb Core_tlb;
typedef Genode::Core_thread_id Core_thread_id;
void init_platform();
}
namespace Kernel
{
/**
* Idle thread entry
*/
static void idle_main() { while (1) ; }
Pd_ids * pd_ids() { return unmanaged_singleton<Pd_ids>(); }
Thread_ids * thread_ids() { return unmanaged_singleton<Thread_ids>(); }
Signal_context_ids * signal_context_ids() { return unmanaged_singleton<Signal_context_ids>(); }
Signal_receiver_ids * signal_receiver_ids() { return unmanaged_singleton<Signal_receiver_ids>(); }
Pd_pool * pd_pool() { return unmanaged_singleton<Pd_pool>(); }
Thread_pool * thread_pool() { return unmanaged_singleton<Thread_pool>(); }
Signal_context_pool * signal_context_pool() { return unmanaged_singleton<Signal_context_pool>(); }
Signal_receiver_pool * signal_receiver_pool() { return unmanaged_singleton<Signal_receiver_pool>(); }
/**
* Access to static kernel timer
*/
static Timer * timer() { static Timer _object; return &_object; }
void reset_lap_time()
{
timer()->start_one_shot(timer()->ms_to_tics(USER_LAP_TIME_MS));
}
/**
* Static kernel PD that describes core
*/
static Pd * core()
{
/**
* Core protection-domain
*/
class Core_pd : public Pd
{
public:
/**
* Constructor
*/
Core_pd(Tlb * const tlb, Platform_pd * const platform_pd)
:
Pd(tlb, platform_pd)
{ }
};
constexpr int tlb_align = 1 << Core_tlb::ALIGNM_LOG2;
Core_tlb * core_tlb = unmanaged_singleton<Core_tlb, tlb_align>();
Core_pd * core_pd = unmanaged_singleton<Core_pd>(core_tlb, nullptr);
return core_pd;
}
/**
* Get core attributes
*/
unsigned core_id() { return core()->id(); }
}
namespace Kernel
{
/**
* Access to static CPU scheduler
*/
Cpu_scheduler * cpu_scheduler()
{
/* create idle thread */
static char idle_stack[DEFAULT_STACK_SIZE]
__attribute__((aligned(Cpu::DATA_ACCESS_ALIGNM)));
static Thread idle(Priority::MAX, "idle");
static bool init = 0;
if (!init) {
enum { STACK_SIZE = sizeof(idle_stack)/sizeof(idle_stack[0]) };
idle.ip = (addr_t)&idle_main;;
idle.sp = (addr_t)&idle_stack[STACK_SIZE];;
idle.init(0, core_id(), 0, 0);
init = 1;
}
/* create CPU scheduler with a permanent idle thread */
static Cpu_scheduler cpu_sched(&idle);
return &cpu_sched;
}
/**
* Get attributes of the mode transition region in every PD
*/
addr_t mode_transition_virt_base() { return mtc()->VIRT_BASE; }
size_t mode_transition_size() { return mtc()->SIZE; }
/**
* Get attributes of the kernel objects
*/
size_t thread_size() { return sizeof(Thread); }
size_t pd_size() { return sizeof(Tlb) + sizeof(Pd); }
size_t signal_context_size() { return sizeof(Signal_context); }
size_t signal_receiver_size() { return sizeof(Signal_receiver); }
unsigned pd_alignm_log2() { return Tlb::ALIGNM_LOG2; }
size_t vm_size() { return sizeof(Vm); }
/**
* Handle an interrupt request
*/
void handle_interrupt()
{
/* determine handling for specific interrupt */
unsigned irq_id;
if (pic()->take_request(irq_id))
{
switch (irq_id) {
case Timer::IRQ: {
cpu_scheduler()->yield();
timer()->clear_interrupt();
reset_lap_time();
break; }
default: {
Irq::occurred(irq_id);
break; }
}
}
/* disengage interrupt controller from IRQ */
pic()->finish_request();
}
}
/**
* Prepare the system for the first run of 'kernel'
*/
extern "C" void init_phys_kernel() {
Cpu::init_phys_kernel(); }
/**
* Kernel main routine
*/
extern "C" void kernel()
{
static bool initial_call = true;
/* an exception occurred */
if (!initial_call)
{
/* handle exception that interrupted the last user */
cpu_scheduler()->head()->handle_exception();
/* kernel initialization */
} else {
/* enable kernel timer */
pic()->unmask(Timer::IRQ);
/* TrustZone initialization code */
trustzone_initialization(pic());
/* enable performance counter */
perf_counter()->enable();
/* switch to core address space */
Cpu::init_virt_kernel(core()->tlb()->base(), core_id());
/*
* From this point on, it is safe to use 'cmpxchg', i.e., to create
* singleton objects via the static-local object pattern. See
* the comment in 'src/base/singleton.h'.
*/
/* create the core main thread */
{
/* get stack memory that fullfills the constraints for core stacks */
enum {
STACK_ALIGNM = 1 << Genode::CORE_STACK_ALIGNM_LOG2,
STACK_SIZE = DEFAULT_STACK_SIZE,
};
if (STACK_SIZE > STACK_ALIGNM - sizeof(Core_thread_id)) {
PERR("stack size does not fit stack alignment of core");
}
static char s[STACK_SIZE] __attribute__((aligned(STACK_ALIGNM)));
/* provide thread ident at the aligned base of the stack */
*(Core_thread_id *)s = 0;
/* start thread with stack pointer at the top of stack */
static Native_utcb utcb;
static Thread t(Priority::MAX, "core");
_main_thread_id = t.id();
_main_thread_utcb = &utcb;
_main_thread_utcb->start_info()->init(t.id(), Genode::Native_capability());
t.ip = (addr_t)CORE_MAIN;;
t.sp = (addr_t)s + STACK_SIZE;
t.init(0, core_id(), &utcb, 1);
}
/* kernel initialization finished */
init_platform();
reset_lap_time();
initial_call = false;
}
/* will jump to the context related mode-switch */
cpu_scheduler()->head()->proceed();
}
Kernel::Mode_transition_control * Kernel::mtc()
{
/* compose CPU context for kernel entry */
struct Kernel_context : Cpu::Context
{
/**
* Constructor
*/
Kernel_context()
{
ip = (addr_t)kernel;
sp = (addr_t)&_kernel_stack_high;
core()->admit(this);
}
} * const k = unmanaged_singleton<Kernel_context>();
/* initialize mode transition page */
return unmanaged_singleton<Mode_transition_control>(k);
}