/* * \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 #include #include #include #include #include #include /* base includes */ #include /* base-hw includes */ #include 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(); } /* 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(); } Thread_ids * thread_ids() { return unmanaged_singleton(); } Signal_context_ids * signal_context_ids() { return unmanaged_singleton(); } Signal_receiver_ids * signal_receiver_ids() { return unmanaged_singleton(); } Pd_pool * pd_pool() { return unmanaged_singleton(); } Thread_pool * thread_pool() { return unmanaged_singleton(); } Signal_context_pool * signal_context_pool() { return unmanaged_singleton(); } Signal_receiver_pool * signal_receiver_pool() { return unmanaged_singleton(); } /** * 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_pd * core_pd = unmanaged_singleton(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(); /* initialize mode transition page */ return unmanaged_singleton(k); }