/* * \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 /* base-hw includes */ #include #include using namespace Kernel; /* get core configuration */ extern Genode::Native_utcb * _main_utcb; extern int _kernel_stack_high; extern "C" void CORE_MAIN(); namespace Kernel { /* import Genode types */ typedef Genode::umword_t umword_t; typedef Genode::Core_tlb Core_tlb; } namespace Kernel { /** * Idle thread entry */ static void idle_main() { while (1) ; } Pd_ids * pd_ids() { return unsynchronized_singleton(); } Thread_ids * thread_ids() { return unsynchronized_singleton(); } Signal_context_ids * signal_context_ids() { return unsynchronized_singleton(); } Signal_receiver_ids * signal_receiver_ids() { return unsynchronized_singleton(); } Pd_pool * pd_pool() { return unsynchronized_singleton(); } Thread_pool * thread_pool() { return unsynchronized_singleton(); } Signal_context_pool * signal_context_pool() { return unsynchronized_singleton(); } Signal_receiver_pool * signal_receiver_pool() { return unsynchronized_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() { constexpr int tlb_align = 1 << Core_tlb::ALIGNM_LOG2; Core_tlb *core_tlb = unsynchronized_singleton(); Pd *pd = unsynchronized_singleton(core_tlb, nullptr); return 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((Platform_thread *)0); static bool init = 0; if (!init) { enum { STACK_SIZE = sizeof(idle_stack)/sizeof(idle_stack[0]) }; void * const ip = (void *)&idle_main; void * const sp = (void *)&idle_stack[STACK_SIZE]; idle.init(ip, sp, 0, core_id(), 0, 0, 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; if (pic()->take_request(irq)) { switch (irq) { case Timer::IRQ: { cpu_scheduler()->yield(); timer()->clear_interrupt(); reset_lap_time(); break; } default: { Irq_receiver * const o = Irq_receiver::receiver(irq); assert(o); o->receive_irq(irq); 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 occured */ if (!initial_call) { /* handle exception that interrupted the last user */ cpu_scheduler()->head()->handle_exception(); /* kernel initialization */ } else { Genode::printf("Kernel started!\n"); /* 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 */ { static Native_utcb utcb; static char stack[DEFAULT_STACK_SIZE] __attribute__((aligned(Cpu::DATA_ACCESS_ALIGNM))); enum { STACK_SIZE = sizeof(stack)/sizeof(stack[0]) + 1 }; void * const ip = (void *)CORE_MAIN; void * const sp = (void *)&stack[STACK_SIZE - 1]; _main_utcb = &utcb; static Thread t((Platform_thread *)0); t.init(ip, sp, 0, core_id(), &utcb, &utcb, 1, 1); } /* kernel initialization finished */ 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 = unsynchronized_singleton(); /* initialize mode transition page */ return unsynchronized_singleton(k); }