/* * \brief Implementations for kernels platform class * \author Martin Stein * \date 2010-10-01 */ /* * Copyright (C) 2010-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. */ #ifndef _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ #define _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ /* Device includes */ #include #include #include /* Kernel includes */ #include #include #include #include /* * Asm labels where to enter an irq/exception/syscall from userland, and vice * versa the userland from inside the kernel */ extern Kernel::word_t _syscall_entry; extern Kernel::word_t _exception_entry; extern Kernel::word_t _interrupt_entry; extern Kernel::word_t _userland_entry; extern Kernel::word_t _atomic_ops_begin; extern Kernel::word_t _atomic_ops_end; extern Kernel::addr_t _call_after_kernel; namespace Kernel { enum { PLATFORM__TRACE = 1, PLATFORM__VERBOSE = 0, PLATFORM__VERBOSE__THREAD_TRACING = 1, PLATFORM_THREAD__ERROR = 1, PLATFORM_THREAD__WARNING = 1, PLATFORM_THREAD__VERBOSE = 0, PLATFORM_IRQ__VERBOSE = 0, PLATFORM_EXCEPTION__VERBOSE = 0, PLATFORM_SYSCALL__VERBOSE = 0, WORD_WIDTH_LOG2 = 5, BYTE_WIDTH_LOG2 = 3 }; class Platform_thread; class Platform; /** * Get kernel's static platform representation */ Platform* platform(); /** * Platform specific execution context */ struct Exec_context { typedef Kernel::Protection_id Protection_id; typedef Kernel::word_t word_t; /** * Type constraints */ enum{ WORD_WIDTH_LOG2 = Kernel::WORD_WIDTH_LOG2, BYTE_WIDTH_LOG2 = Kernel::BYTE_WIDTH_LOG2, WORD_SIZE = 1 << (WORD_WIDTH_LOG2-BYTE_WIDTH_LOG2) }; /** * Blocking types */ enum{ NO_BLOCKING = 0, IRQ_BLOCKING = 1, EXCEPTION_BLOCKING = 2, SYSCALL_BLOCKING = 3, BLOCKING_TYPE_RANGE = 4 }; /** * Register constraints */ enum { /* rmsr */ RMSR_BE_LSHIFT = 0, RMSR_BE_MASK = 1 << RMSR_BE_LSHIFT, RMSR_IE_LSHIFT = 1, RMSR_IE_MASK = 1 << RMSR_IE_LSHIFT, RMSR_C_LSHIFT = 2, RMSR_C_MASK = 1 << RMSR_C_LSHIFT, RMSR_BIP_LSHIFT = 3, RMSR_BIP_MASK = 1 << RMSR_BIP_LSHIFT, RMSR_FSL_LSHIFT = 4, RMSR_FSL_MASK = 1 << RMSR_FSL_LSHIFT, RMSR_ICE_LSHIFT = 5, RMSR_ICE_MASK = 1 << RMSR_ICE_LSHIFT, RMSR_DZ_LSHIFT = 6, RMSR_DZ_MASK = 1 << RMSR_DZ_LSHIFT, RMSR_DCE_LSHIFT = 7, RMSR_DCE_MASK = 1 << RMSR_DCE_LSHIFT, RMSR_EE_LSHIFT = 8, RMSR_EE_MASK = 1 << RMSR_EE_LSHIFT, RMSR_EIP_LSHIFT = 9, RMSR_EIP_MASK = 1 << RMSR_EIP_LSHIFT, RMSR_PVR_LSHIFT = 10, RMSR_PVR_MASK = 1 << RMSR_PVR_LSHIFT, RMSR_UM_LSHIFT = 11, RMSR_UM_MASK = 1 << RMSR_UM_LSHIFT, RMSR_UMS_LSHIFT = 12, RMSR_UMS_MASK = 1 << RMSR_UMS_LSHIFT, RMSR_VM_LSHIFT = 13, RMSR_VM_MASK = 1 << RMSR_VM_LSHIFT, RMSR_VMS_LSHIFT = 14, RMSR_VMS_MASK = 1 << RMSR_VMS_LSHIFT, RMSR_CC_LSHIFT = 31, RMSR_CC_MASK = 1 << RMSR_CC_LSHIFT, /* resr */ RESR_EC_LSHIFT = 0, RESR_EC_MASK = 0x1F<> RESR_EC_LSHIFT; } }; } extern Kernel::Exec_context* _userland_context; namespace Kernel { /** * Platform representation */ class Platform : public Kernel_entry::Listener { public: /** * General configuration */ enum { ATOMIC_OPS_PAGE_SIZE_LOG2 = DEFAULT_PAGE_SIZE_LOG2, KERNEL_ENTRY_SIZE_LOG2 = DEFAULT_PAGE_SIZE_LOG2 }; /** * Verbose, errors, warnings */ enum { VERBOSE__CONSTRUCTOR = PLATFORM__VERBOSE, VERBOSE__ENTER_USERLAND = PLATFORM__VERBOSE }; /** * General platform constraints */ enum { WORD_WIDTH_LOG2 = Kernel::WORD_WIDTH_LOG2, BYTE_WIDTH_LOG2 = Kernel::BYTE_WIDTH_LOG2, BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2, WORD_WIDTH = 1 << WORD_WIDTH_LOG2, WORD_SIZE = 1 << (WORD_WIDTH_LOG2-BYTE_WIDTH_LOG2), WORD_HALFWIDTH = WORD_WIDTH >> 1, WORD_LEFTHALF_MASK = ~0 << WORD_HALFWIDTH , WORD_RIGHTHALF_MASK = ~WORD_LEFTHALF_MASK }; typedef uint32_t word_t; typedef uint32_t Register; private: Kernel::Tlb _tlb; /** * Processor specific */ enum { ASM_IMM = 0xb0000000, ASM_BRAI = 0xb8080000, ASM_RTSD = 0xb6000000, ASM_NOP = 0x80000000, SYSCALL_ENTRY = 0x00000008, INTERRUPT_ENTRY = 0x00000010, EXCEPTION_ENTRY = 0x00000020 }; void _initial_tlb_entries() { using namespace Paging; Physical_page::size_t atomic_ops_pps, kernel_entry_pps; if (Physical_page::size_by_size_log2( atomic_ops_pps, ATOMIC_OPS_PAGE_SIZE_LOG2) || Physical_page::size_by_size_log2( kernel_entry_pps, KERNEL_ENTRY_SIZE_LOG2)) { printf("Error in Kernel::Platform::_initial_tlb_entries"); return; }; Physical_page atomic_ops_pp((addr_t)&_atomic_ops_begin, atomic_ops_pps, Physical_page::RX); Virtual_page atomic_ops_vp(atomic_ops_pp.address(), UNIVERSAL_PROTECTION_ID); Resolution atomic_ops_res(&atomic_ops_vp, &atomic_ops_pp); Physical_page kernel_entry_pp((addr_t)0, kernel_entry_pps, Physical_page::RX); Virtual_page kernel_entry_vp(kernel_entry_pp.address(), UNIVERSAL_PROTECTION_ID); Resolution kernel_entry_res(&kernel_entry_vp, &kernel_entry_pp); tlb()->add_fixed(&atomic_ops_res, &kernel_entry_res); } /** * Initialize the ability to enter userland */ inline void _init_userland_entry() { _call_after_kernel=(addr_t)&_userland_entry; } /** * Fill in jump to CPU's 2-word-width exception entry */ inline void _init_exception_entry() { *(word_t*)EXCEPTION_ENTRY = ASM_IMM | ((word_t)&_exception_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; *(word_t*)(EXCEPTION_ENTRY + WORD_SIZE) = ASM_BRAI | ((word_t)&_exception_entry & WORD_RIGHTHALF_MASK); } /** * Fill in jump to CPU's 2-word-width syscall entry */ inline void _init_syscall_entry() { *(word_t*)SYSCALL_ENTRY = ASM_IMM | ((word_t)&_syscall_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; *(word_t*)(SYSCALL_ENTRY + WORD_SIZE) = ASM_BRAI | (word_t) &_syscall_entry & WORD_RIGHTHALF_MASK; } /** * Fill in jump to CPU's 2-word-width interrupt entry */ inline void _init_interrupt_entry() { *((word_t*) INTERRUPT_ENTRY) = ASM_IMM | ((word_t)&_interrupt_entry & WORD_LEFTHALF_MASK) >> WORD_HALFWIDTH; *((word_t*)(INTERRUPT_ENTRY + WORD_SIZE)) = ASM_BRAI | (word_t) &_interrupt_entry & WORD_RIGHTHALF_MASK; } public: /** * Constructor */ Platform(); bool is_atomic_operation(void* ip) { enum { SIZE = (1 << ATOMIC_OPS_PAGE_SIZE_LOG2), SIZE_WORDS = SIZE/sizeof(word_t) }; static word_t *const _first_atomic_op = &_atomic_ops_begin; static word_t *const _last_atomic_op = _first_atomic_op + (SIZE_WORDS-1); return ((ip >=_first_atomic_op) & (ip <=_last_atomic_op)); } /** * Set execution context loaded at next userland entry */ inline int userland_context(Exec_context* c) { _userland_context = c; _userland_context__verbose__set(c); return 0; } /** * Lock the platforms execution ability to one execution context */ inline void lock(Exec_context *c){ userland_context(c); } /** * Set return address register * * It is essential that this function is always inline! */ inline int return_address(addr_t a) { asm volatile("add r15, %0, r0"::"r"((Register)a):); return 0; } /** * Halt whole system */ inline void halt() { asm volatile ("bri 0"); }; /** * Get the platforms general IRQ-controller */ inline Irq_controller * const irq_controller(); /** * Get the timer that is reserved for kernels schedulinge */ inline Scheduling_timer * const timer(); Tlb *tlb() { return &_tlb; }; protected: void _on_kernel_entry__trace__thread_interrupts(); void _on_kernel_entry__verbose__called() { if (PLATFORM__VERBOSE) printf("Kernel::Platform::_on_kernel_entry\n"); } void _on_kernel_entry(); void _userland_context__verbose__set(Exec_context* c) { if (!PLATFORM__VERBOSE) return; if (_userland_context) { printf("Kernel::Platform::_userland_context, new userland context c=0x%8X, printing contents", (uint32_t)_userland_context); c->print_content(2); } else printf("Kernel::Platform::_userland_context, no userland context"); printf("\n"); } }; class Platform_blocking { protected: typedef Kernel::Exec_context Context; typedef Kernel::Platform_thread Owner; Owner* _owner; Context* _context; public: /** * Constructor */ Platform_blocking(Owner* o, Context* c) : _owner(o), _context(c) {} }; /** * Platform-specific IRQ */ class Platform_irq : public Platform_blocking, public Irq { public: /** * Constructor */ Platform_irq(Owner* o, Context* c) : Platform_blocking(o,c) {} void block(); protected: void _block__verbose__success() { if (PLATFORM_IRQ__VERBOSE) printf("Platform_irq::block(), _id=%i\n", _id); } }; class Platform_exception : public Platform_blocking, public Exception { protected: Protection_id protection_id(); addr_t address(); bool attempted_write_access(); public: /** * Constructor */ Platform_exception(Owner* o, Context* c) : Platform_blocking(o, c) { } void block(Exec_context * c); }; class Platform_syscall : public Platform_blocking, public Syscall { public: /** * Constructor */ Platform_syscall(Owner *o, Context *c, Source *s) : Platform_blocking(o,c), Syscall(&c->r30, &c->r29, &c->r28, &c->r27, &c->r26, &c->r25, &c->r24, &c->r30, s) { } void block(); protected: void _block__verbose__success() { if (PLATFORM_IRQ__VERBOSE) printf("Platform_syscall::block(), _id=%i\n", _id); } }; /** * Platform-specific thread implementations */ class Platform_thread { typedef Kernel::Blocking Blocking; enum { INITIAL_RMSR = 1 << Exec_context::RMSR_PVR_LSHIFT | 1 << Exec_context::RMSR_UMS_LSHIFT | 1 << Exec_context::RMSR_VMS_LSHIFT, INITIAL_BLOCKING_TYPE = Exec_context::NO_BLOCKING }; Platform_irq _irq; Platform_exception _exception; Platform_syscall _syscall; Exec_context _exec_context; /* if not zero, this thread is blocked */ Blocking* _blocking; public: bool timer_interrupted() { return false; } void yield_after_atomic_operation() { _exec_context.r31 = 1; } void unblock() { _blocking = 0; } typedef Kernel::Protection_id Protection_id; Platform_thread() : _irq(this, &_exec_context), _exception(this, &_exec_context), _syscall(this, &_exec_context, 0), _exec_context(this), _blocking(0) { } Platform_thread(addr_t ip, addr_t sp, Protection_id pid, Syscall::Source *sc) : _irq(this, &_exec_context), _exception(this, &_exec_context), _syscall(this, &_exec_context, sc), _exec_context(this), _blocking(0) { _exec_context.rpc = ip; _exec_context.r1 = sp; _exec_context.rpid = pid; _exec_context.blocking_type= INITIAL_BLOCKING_TYPE; _exec_context.rmsr = INITIAL_RMSR; } enum { BLOCK__ERROR = 1, BLOCK__WARNING = 1}; /** * Get thread blocked if there is a blocking at execution context */ void on_kernel_entry() { using Kernel::Exec_context; switch (_exec_context.blocking_type) { case Exec_context::NO_BLOCKING: _blocking = 0; break; case Exec_context::IRQ_BLOCKING: _irq.block(); _blocking = &_irq; break; case Exec_context::EXCEPTION_BLOCKING: _exception.block(&_exec_context); _blocking = &_exception; break; case Exec_context::SYSCALL_BLOCKING: _syscall.block(); _blocking = &_syscall; break; default: _block__error__unknown_blocking_type(); } _block__verbose__success(); } Protection_id protection_id() { return (Protection_id)_exec_context.rpid; } addr_t instruction_pointer() { return (addr_t)_exec_context.rpc; } Exec_context* exec_context() { return &_exec_context; } Exec_context* unblocked_exec_context() { Exec_context* result=&_exec_context; if (_blocking) { if (!_blocking->unblock()) result = 0; else _blocking = 0; } return result; } void call_argument_0(word_t value){ _exec_context.r5=value;} void bootstrap_argument_0(word_t value){ _exec_context.r31=value;} void print_state() { _exec_context.print_content(2); printf("\n"); }; Exception *exception() { return &_exception; } Syscall *syscall() { return &_syscall; } Irq *irq() { return &_irq; } protected: void _block__error__unknown_blocking_type() { if (!PLATFORM_THREAD__ERROR) return; printf("Error in Kernel::Platform_thread::block: " "unknown blocking_type=%i, printing state\n", _exec_context.blocking_type); _exec_context.print_content(2); printf("halt\n"); halt(); } void _block__warning__no_blocking() { if (!PLATFORM_THREAD__WARNING) return; printf("Warning Kernel::Platform_thread::_no_blocking called\n"); halt(); } void _block__verbose__success() { if (!PLATFORM_THREAD__VERBOSE) return; printf("Kernel::Platform_thread::block, blocked " "this=0x%p, blocking_type=%i\n", this, _exec_context.blocking_type); } }; } Kernel::Irq_controller * const Kernel::Platform::irq_controller() { using namespace Xilinx; static Irq_controller _ic = Irq_controller(Xps_intc::Constr_arg(Cpu::XPS_INTC_BASE)); return &_ic; } Kernel::Scheduling_timer * const Kernel::Platform::timer() { using namespace Xilinx; static Scheduling_timer _st = Scheduling_timer(SCHEDULING_TIMER_IRQ, (addr_t)SCHEDULING_TIMER_BASE); return &_st; } #endif /* _INCLUDE__PETALOGIX_S3ADSP1800_MMU__PLATFORM__PLATFORM_H_ */