diff --git a/repos/base-hw/include/spec/riscv/cpu/atomic.h b/repos/base-hw/include/spec/riscv/cpu/atomic.h new file mode 100644 index 000000000..02d626318 --- /dev/null +++ b/repos/base-hw/include/spec/riscv/cpu/atomic.h @@ -0,0 +1,56 @@ +/* + * \brief Atomic operations for RISCV + * \author Sebastian Sumpf + * \date 2015-06-01 + */ + +/* + * Copyright (C) 2015-2016 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__RISCV__CPU__ATOMIC_H_ +#define _INCLUDE__RISCV__CPU__ATOMIC_H_ + +/* Genode includes */ +#include + +namespace Genode { + + /** + * Atomic compare and exchange + * + * This function compares the value at dest with cmp_val. + * If both values are equal, dest is set to new_val. If + * both values are different, the value at dest remains + * unchanged. + * + * Note, that cmpxchg() represents a memory barrier. + * + * \return 1 if the value was successfully changed to new_val, + * 0 if cmp_val and the value at dest differ. + */ + inline int cmpxchg(volatile int *dest, int cmp_val, int new_val) + { + int old_val; + + __asm__ __volatile__( + " 1: \n" + " lr.w %0, (%1) \n" + " bne %0, %2, 2f \n" + " sc.w %0, %3, (%1) \n" + " bnez %0, 1b \n" + " mv %0, %2 \n" + " 2: \n" + : "=&r" (old_val) + : "r" (dest), "r" (cmp_val), "r" (new_val) + : "memory"); + + Genode::memory_barrier(); + return old_val == cmp_val ? 1 : 0; + } +} + +#endif /* _INCLUDE__RISCV__CPU__ATOMIC_H_ */ diff --git a/repos/base-hw/include/spec/riscv/cpu/consts.h b/repos/base-hw/include/spec/riscv/cpu/consts.h new file mode 100644 index 000000000..11a945abf --- /dev/null +++ b/repos/base-hw/include/spec/riscv/cpu/consts.h @@ -0,0 +1,35 @@ +/* + * \brief Constants definitions for the RISCV architecture. + * \author Sebastian Sumpf + * \date 2015-06-01 + */ + +/* + * Copyright (C) 2015-2016 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__RISCV__CPU__CONSTS_H_ +#define _INCLUDE__RISCV__CPU__CONSTS_H_ + +/* Genode includes */ +#include + +namespace Abi { + + /* + * On RISC-V we align the stack top to 16-byte. As a call (or branch) will + * not change the stack pointer, we need no further stack adjustment. + */ + static Genode::addr_t stack_align(Genode::addr_t addr) { + return (addr & ~0xf); } + + /** + * Do ABI specific initialization to a freshly created stack + */ + inline void init_stack(Genode::addr_t) { } +} + +#endif /* _INCLUDE__RISCV__CPU__CONSTS_H_ */ diff --git a/repos/base-hw/include/spec/riscv/cpu/cpu_state.h b/repos/base-hw/include/spec/riscv/cpu/cpu_state.h new file mode 100644 index 000000000..e6b43b109 --- /dev/null +++ b/repos/base-hw/include/spec/riscv/cpu/cpu_state.h @@ -0,0 +1,45 @@ +/** + * \brief CPU state + * \author Sebastian Sumpf + * \date 2015-06-01 + */ + +/* + * Copyright (C) 2015-2016 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__RISCV__CPU__CPU_STATE_H_ +#define _INCLUDE__RISCV__CPU__CPU_STATE_H_ + +/* Genode includes */ +#include + +namespace Genode { struct Cpu_state; } + +struct Genode::Cpu_state +{ + enum Cpu_exception { + INSTRUCTION_UNALIGNED = 0, + INSTRUCTION_PAGE_FAULT = 1, + INSTRUCTION_ILLEGAL = 2, + LOAD_UNALIGNED = 4, + LOAD_PAGE_FAULT = 5, + STORE_UNALIGNED = 6, + STORE_PAGE_FAULT = 7, + SUPERVISOR_CALL = 8, + RESET = 16, + IRQ_FLAG = 1UL << 63, + }; + + addr_t ip, cpu_exception, ra, sp, gp, tp, t0, t1, t2, s0, s1, a0, a1, a2, + a3, a4, a5, a6, a7, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, + t4, t5, t6; + + bool is_irq() { return cpu_exception & IRQ_FLAG; } + unsigned irq() { return cpu_exception ^ IRQ_FLAG; } +}; + +#endif /* _INCLUDE__RISCV__CPU__CPU_STATE_H_ */ diff --git a/repos/base-hw/include/spec/riscv/cpu/memory_barrier.h b/repos/base-hw/include/spec/riscv/cpu/memory_barrier.h new file mode 100644 index 000000000..66e6869e0 --- /dev/null +++ b/repos/base-hw/include/spec/riscv/cpu/memory_barrier.h @@ -0,0 +1,25 @@ +/** + * \brief Memory barrier + * \author Sebastian Sumpf + * \date 2015-06-01 + */ + +/* + * Copyright (C) 2015-2016 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__RISCV__CPU__MEMORY_BARRIER_H_ +#define _INCLUDE__RISCV__CPU__MEMORY_BARRIER_H_ + +namespace Genode { + + static inline void memory_barrier() + { + asm volatile ("fence" ::: "memory"); + } +} + +#endif /* _INCLUDE__RISCV__CPU__MEMORY_BARRIER_H_ */ diff --git a/repos/base-hw/include/spec/riscv/cpu/string.h b/repos/base-hw/include/spec/riscv/cpu/string.h new file mode 100644 index 000000000..61c072557 --- /dev/null +++ b/repos/base-hw/include/spec/riscv/cpu/string.h @@ -0,0 +1,32 @@ +/* + * \brief CPU-specific memcpy + * \author Sebastian Sumpf + * \date 2015-06-01 + */ + +/* + * Copyright (C) 2015-2016 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__RISCV__CPU__STRING_H_ +#define _INCLUDE__RISCV__CPU__STRING_H_ + +namespace Genode { + + /** + * Copy memory block + * + * \param dst destination memory block + * \param src source memory block + * \param size number of bytes to copy + * + * \return number of bytes not copied + */ + inline size_t memcpy_cpu(void *, const void *, size_t size) { + return size; } +} + +#endif /* _INCLUDE__RISCV__CPU__STRING_H_ */ diff --git a/repos/base-hw/include/spec/riscv/kernel/interface_support.h b/repos/base-hw/include/spec/riscv/kernel/interface_support.h new file mode 100644 index 000000000..c97d80dc8 --- /dev/null +++ b/repos/base-hw/include/spec/riscv/kernel/interface_support.h @@ -0,0 +1,35 @@ +/* + * \brief Interface between kernel and userland + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 _KERNEL__INTERFACE_SUPPORT_H_ +#define _KERNEL__INTERFACE_SUPPORT_H_ + +/* Genode includes */ +#include + +namespace Kernel +{ + typedef Genode::uint64_t Call_arg; + typedef Genode::uint64_t Call_ret; + + /** + * Events that are provided by a kernel thread-object for user handling + */ + struct Thread_event_id + { + enum { FAULT = 0 }; + }; +} + +#endif /* _KERNEL__INTERFACE_SUPPORT_H_ */ + diff --git a/repos/base-hw/lib/mk/spec/riscv/base-common.mk b/repos/base-hw/lib/mk/spec/riscv/base-common.mk new file mode 100644 index 000000000..ae4f01399 --- /dev/null +++ b/repos/base-hw/lib/mk/spec/riscv/base-common.mk @@ -0,0 +1,3 @@ +include $(REP_DIR)/lib/mk/base-common.inc + +vpath kernel/interface.cc $(REP_DIR)/src/base/riscv diff --git a/repos/base-hw/lib/mk/spec/riscv/core.mk b/repos/base-hw/lib/mk/spec/riscv/core.mk new file mode 100644 index 000000000..4d8066209 --- /dev/null +++ b/repos/base-hw/lib/mk/spec/riscv/core.mk @@ -0,0 +1,21 @@ +INC_DIR += $(REP_DIR)/src/core/include/spec/riscv + +CC_OPT += -fno-delete-null-pointer-checks -msoft-float + +# add C++ sources +SRC_CC += platform_services.cc +SRC_CC += kernel/vm_thread.cc kernel/kernel.cc +SRC_CC += spec/riscv/kernel/cpu_context.cc +SRC_CC += spec/riscv/kernel/thread.cc +SRC_CC += spec/riscv/kernel/pd.cc +SRC_CC += spec/riscv/kernel/cpu.cc +SRC_CC += spec/riscv/kernel/exception_vector.cc +SRC_CC += spec/riscv/platform_support.cc + +#add assembly sources +SRC_S += spec/riscv/mode_transition.s +SRC_S += spec/riscv/kernel/crt0.s +SRC_S += spec/riscv/crt0.s + +# include less specific configuration +include $(REP_DIR)/lib/mk/core.inc diff --git a/repos/base-hw/lib/mk/spec/riscv/startup.mk b/repos/base-hw/lib/mk/spec/riscv/startup.mk new file mode 100644 index 000000000..21efabf59 --- /dev/null +++ b/repos/base-hw/lib/mk/spec/riscv/startup.mk @@ -0,0 +1,3 @@ +include $(call select_from_repositories,lib/mk/startup.inc) + +vpath crt0.s $(REP_DIR)/src/lib/startup/spec/riscv diff --git a/repos/base-hw/mk/spec/hw_riscv.mk b/repos/base-hw/mk/spec/hw_riscv.mk new file mode 100644 index 000000000..ca8b80645 --- /dev/null +++ b/repos/base-hw/mk/spec/hw_riscv.mk @@ -0,0 +1,9 @@ +SPECS += hw riscv platform_riscv 64bit + +LD_TEXT_ADDR ?= 0x1000 +CORE_LD_TEXT_ADDR = 0x200 +NR_OF_CPUS = 1 +REP_INC_DIR += include/spec/riscv + +include $(call select_from_repositories,mk/spec/64bit.mk) +include $(call select_from_repositories,mk/spec/hw.mk) diff --git a/repos/base-hw/src/base/riscv/kernel/interface.cc b/repos/base-hw/src/base/riscv/kernel/interface.cc new file mode 100644 index 000000000..5eea57627 --- /dev/null +++ b/repos/base-hw/src/base/riscv/kernel/interface.cc @@ -0,0 +1,102 @@ +/* + * \brief Interface between kernel and userland + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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. + */ + +/* Genode includes */ +#include +#include + +using namespace Kernel; + + +/************************************ + ** Helper macros for kernel calls ** + ************************************/ + +#define CALL_1_FILL_ARG_REGS \ + register Call_arg arg_0_reg asm("a0") = arg_0; + +#define CALL_2_FILL_ARG_REGS \ + CALL_1_FILL_ARG_REGS \ + register Call_arg arg_1_reg asm("a1") = arg_1; + +#define CALL_3_FILL_ARG_REGS \ + CALL_2_FILL_ARG_REGS \ + register Call_arg arg_2_reg asm("a2") = arg_2; + +#define CALL_4_FILL_ARG_REGS \ + CALL_3_FILL_ARG_REGS \ + register Call_arg arg_3_reg asm("a3") = arg_3; + +#define CALL_5_FILL_ARG_REGS \ + CALL_4_FILL_ARG_REGS \ + register Call_arg arg_4_reg asm("a4") = arg_4; + +#define CALL_1_SWI "ecall\n" : "+r" (arg_0_reg) +#define CALL_2_SWI CALL_1_SWI: "r" (arg_1_reg) +#define CALL_3_SWI CALL_2_SWI, "r" (arg_2_reg) +#define CALL_4_SWI CALL_3_SWI, "r" (arg_3_reg) +#define CALL_5_SWI CALL_4_SWI, "r" (arg_4_reg) + + +/****************** + ** Kernel calls ** + ******************/ + +Call_ret Kernel::call(Call_arg arg_0) +{ + CALL_1_FILL_ARG_REGS + asm volatile(CALL_1_SWI); + return arg_0_reg; +} + + +Call_ret Kernel::call(Call_arg arg_0, + Call_arg arg_1) +{ + CALL_2_FILL_ARG_REGS + asm volatile(CALL_2_SWI); + return arg_0_reg; +} + + +Call_ret Kernel::call(Call_arg arg_0, + Call_arg arg_1, + Call_arg arg_2) +{ + CALL_3_FILL_ARG_REGS + asm volatile(CALL_3_SWI); + return arg_0_reg; +} + + +Call_ret Kernel::call(Call_arg arg_0, + Call_arg arg_1, + Call_arg arg_2, + Call_arg arg_3) +{ + CALL_4_FILL_ARG_REGS + asm volatile(CALL_4_SWI); + return arg_0_reg; +} + + +Call_ret Kernel::call(Call_arg arg_0, + Call_arg arg_1, + Call_arg arg_2, + Call_arg arg_3, + Call_arg arg_4) +{ + CALL_5_FILL_ARG_REGS + asm volatile(CALL_5_SWI); + return arg_0_reg; +} diff --git a/repos/base-hw/src/core/include/kernel/cpu.h b/repos/base-hw/src/core/include/kernel/cpu.h index 435a2460a..bdc193e18 100644 --- a/repos/base-hw/src/core/include/kernel/cpu.h +++ b/repos/base-hw/src/core/include/kernel/cpu.h @@ -332,7 +332,13 @@ class Kernel::Cpu_pool private: Timer _timer; - char _cpus[NR_OF_CPUS][sizeof(Cpu)]; + + /* + * Align to machine word size, otherwise load/stores might fail on some + * platforms. + */ + char _cpus[NR_OF_CPUS][sizeof(Cpu)] + __attribute__((aligned(sizeof(addr_t)))); public: diff --git a/repos/base-hw/src/core/include/spec/riscv/board.h b/repos/base-hw/src/core/include/spec/riscv/board.h new file mode 100644 index 000000000..049b7f768 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/board.h @@ -0,0 +1,25 @@ +/* + * \brief Board spcecification + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 _BOARD_H_ +#define _BOARD_H_ + +namespace Genode +{ + struct Board + { + void init() { } + }; +} + +#endif /* _BOARD_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/cpu.h b/repos/base-hw/src/core/include/spec/riscv/cpu.h new file mode 100644 index 000000000..12e1c6d02 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/cpu.h @@ -0,0 +1,159 @@ +/* + * \brief CPU driver for core + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 _CPU_H_ +#define _CPU_H_ + +/* Genode includes */ +#include +#include + +namespace Genode +{ + /** + * CPU driver for core + */ + class Cpu; + + typedef __uint128_t sizet_arithm_t; +} + +namespace Kernel { class Pd; } + +class Genode::Cpu +{ + public: + + static constexpr addr_t mtc_size = 0x1000; + static constexpr addr_t exception_entry = (~0ULL) & ~(0xfff); + + /** + * Extend basic CPU state by members relevant for 'base-hw' only + */ + struct Context : Cpu_state + { + addr_t sasid = 0; + addr_t sptbr = 0; /* supervisor page table register */ + + /** + * Return base of assigned translation table + */ + addr_t translation_table() const { return sptbr; } + + /** + * Assign translation-table base 'table' + */ + void translation_table(addr_t const table) { sptbr = table; } + + /** + * Assign protection domain + */ + void protection_domain(Genode::uint8_t const id) { sasid = id; } + }; + + struct Pd + { + Genode::uint8_t asid; /* address space id */ + + Pd(Genode::uint8_t id) : asid(id) {} + }; + + /** + * A usermode execution state + */ + struct User_context : Context + { + /** + * Constructor + */ + User_context(); + + /** + * Support for kernel calls + */ + void user_arg_0(unsigned const arg) { a0 = arg; } + void user_arg_1(unsigned const arg) { a1 = arg; } + void user_arg_2(unsigned const arg) { a2 = arg; } + void user_arg_3(unsigned const arg) { a3 = arg; } + void user_arg_4(unsigned const arg) { a4 = arg; } + addr_t user_arg_0() const { return a0; } + addr_t user_arg_1() const { return a1; } + addr_t user_arg_2() const { return a2; } + addr_t user_arg_3() const { return a3; } + addr_t user_arg_4() const { return a4; } + + /** + * Initialize thread context + * + * \param table physical base of appropriate translation table + * \param pd_id kernel name of appropriate protection domain + */ + void init_thread(addr_t const table, unsigned const pd_id) + { + protection_domain(pd_id); + translation_table(table); + } + }; + + static void wait_for_interrupt() { asm volatile ("wfi"); }; + + /** + * Post processing after a translation was added to a translation table + * + * \param addr virtual address of the translation + * \param size size of the translation + */ + static void translation_added(addr_t const addr, size_t const size) + { + PDBG("not impl"); + } + + /** + * Return kernel name of the executing CPU + */ + static unsigned executing_id() { return primary_id(); } + + /** + * Return kernel name of the primary CPU + */ + static unsigned primary_id() { return 0; } + + static void flush_tlb_by_pid(unsigned const pid) + { + PDBG("not impl"); + } + + static addr_t sbadaddr() + { + addr_t addr; + asm volatile ("csrr %0, sbadaddr\n" : "=r"(addr)); + return addr; + } + + static void data_synchronization_barrier() { + asm volatile ("fence\n" : : : "memory"); } + + /************* + ** Dummies ** + *************/ + + void switch_to(User_context&) { } + static void prepare_proceeding(Cpu_lazy_state *, Cpu_lazy_state *) { } + static void invalidate_instr_caches() { } + static void invalidate_data_caches() { } + static void flush_data_caches() { } + static void flush_data_caches_by_virt_region(addr_t, size_t) { } + static void invalidate_instr_caches_by_virt_region(addr_t, size_t) { } +}; + +#endif /* _CPU_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/kernel/thread_base.h b/repos/base-hw/src/core/include/spec/riscv/kernel/thread_base.h new file mode 100644 index 000000000..3214d9165 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/kernel/thread_base.h @@ -0,0 +1,43 @@ +/* + * \brief Hardware specific base of kernel thread-objects + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 _KERNEL__THREAD_BASE_H_ +#define _KERNEL__THREAD_BASE_H_ + +/* core includes */ +#include + +namespace Kernel { class Thread_base; } + +/** + * Hardware specific base of kernel thread-objects + */ +class Kernel::Thread_base +{ + protected: + + Thread_event _fault; + addr_t _fault_pd; + addr_t _fault_addr; + addr_t _fault_writes; + addr_t _fault_signal; + + /** + * Constructor + * + * \param t generic part of kernel thread-object + */ + Thread_base(Thread * const t); +}; + +#endif /* _KERNEL__THREAD_BASE_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/machine_call.h b/repos/base-hw/src/core/include/spec/riscv/machine_call.h new file mode 100644 index 000000000..82228b008 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/machine_call.h @@ -0,0 +1,36 @@ +/** + * \brief Calls supported by machine mode (or SBI interface in RISC-V) + * \author Sebastian Sumpf + * \date 2015-06-14 + */ + +/* + * Copyright (C) 2015-2016 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 _MACHINE_CALL_H_ +#define _MACHINE_CALL_H_ + +#include + +namespace Machine { + + enum Call { + PUT_CHAR = 0x100, /* output character */ + SET_SYS_TIMER = 0x101, /* set timer */ + IS_USER_MODE = 0x102, /* check if we are in user mode */ + }; + + inline void call(Call const number, Genode::addr_t const arg0) + { + register Genode::addr_t a0 asm("a0") = number;; + register Genode::addr_t a1 asm("a1") = arg0; + + asm volatile ("ecall\n" : : "r"(a0), "r"(a1)); + } +} + +#endif /* _MACHINE_CALL_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/macros.s b/repos/base-hw/src/core/include/spec/riscv/macros.s new file mode 100644 index 000000000..131b010fc --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/macros.s @@ -0,0 +1,21 @@ +/* + * \brief Macros that are used by multiple assembly files + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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. + */ + + +/*************************************************** + ** Constant values that are pretty commonly used ** + ***************************************************/ + +/* alignment constraints */ +.set DATA_ACCESS_ALIGNM_LOG2, 2 +.set MIN_PAGE_SIZE_LOG2, 12 diff --git a/repos/base-hw/src/core/include/spec/riscv/pic.h b/repos/base-hw/src/core/include/spec/riscv/pic.h new file mode 100644 index 000000000..5d75d6f85 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/pic.h @@ -0,0 +1,73 @@ +/* + * \brief Programmable interrupt controller for core + * \author Sebastian Sumpf + * \date 2015-06-02 + * + * There currently is no interrupt controller defined for the RISC-V platform. + */ + +/* + * Copyright (C) 2015-2016 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 _PIC_H_ +#define _PIC_H_ + +#include + +namespace Genode +{ + /** + * Programmable interrupt controller for core + */ + class Pic; +} + +class Genode::Pic +{ + public: + + enum { + /* + * FIXME: dummy ipi value on non-SMP platform, should be removed + * when SMP is an aspect of CPUs only compiled where necessary + */ + IPI = 0, + NR_OF_IRQ = 15, + }; + + /** + * Constructor + */ + Pic() { } + + /** + * Receive a pending request number 'i' + */ + bool take_request(unsigned & i) { + return true; + } + + /** + * Unmask interrupt 'i' + */ + void unmask(unsigned const i, unsigned) { } + + /** + * Mask interrupt 'i' + */ + void mask(unsigned const i) { } + + /************* + ** Dummies ** + *************/ + + void finish_request() { } +}; + +namespace Kernel { class Pic : public Genode::Pic { }; } + +#endif /* _PIC_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/serial.h b/repos/base-hw/src/core/include/spec/riscv/serial.h new file mode 100644 index 000000000..f192dde0b --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/serial.h @@ -0,0 +1,53 @@ +/* + * \brief Serial output driver for core + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 _SERIAL_H_ +#define _SERIAL_H_ + +/* Genode includes */ +#include +#include + +/* core includes */ +#include + +namespace Genode { class Serial; } + +/** + * Serial output driver for core + */ +class Genode::Serial +{ + public: + + /** + * Constructor + */ + Serial(unsigned) { } + + void put_char(char const c) + { + struct Arg : Register<64> + { + struct Char : Bitfield<0, 8> { }; + struct Write_cmd : Bitfield<48, 1> { }; + struct Stdout : Bitfield<56, 1> { }; + }; + + Machine::call(Machine::PUT_CHAR, Arg::Char::bits(c) | + Arg::Stdout::bits(1) | + Arg::Write_cmd::bits(1)); + } +}; + +#endif /* _SERIAL_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/timer.h b/repos/base-hw/src/core/include/spec/riscv/timer.h new file mode 100644 index 000000000..926f11133 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/timer.h @@ -0,0 +1,98 @@ +/* + * \brief Timer driver for core + * \author Sebastian Sumpf + * \date 2015-08-22 + */ + +/* + * Copyright (C) 2015-2016 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 _TIMER_H_ +#define _TIMER_H_ + +#include +#include + +#include + +namespace Genode +{ + /** + * Timer driver for core + */ + class Timer; +} + +struct Genode::Timer +{ + private: + + addr_t _timeout = 0; + + addr_t _stime() + { + addr_t t; + asm volatile ("csrr %0, stime\n" : "=r"(t)); + return t; + } + + public: + + Timer() + { + /* enable timer interrupt */ + enum { STIE = 0x20 }; + asm volatile ("csrs sie, %0" : : "r"(STIE)); + } + + enum { + SPIKE_TIMER_HZ = 500000, + MS_TICS = SPIKE_TIMER_HZ / 1000, + }; + + /** + * Start single timeout run + * + * \param tics delay of timer interrupt + */ + void start_one_shot(unsigned const tics, unsigned /* cpu */) + { + _timeout = _stime() + tics; + asm volatile ("csrw stimecmp, %0" : : "r"(_timeout)); + } + + /** + * Translate milliseconds to a native timer value + */ + unsigned ms_to_tics(unsigned const ms) + { + return ms * MS_TICS; + } + + /** + * Translate native timer value to milliseconds + */ + unsigned tics_to_ms(unsigned const tics) + { + return tics / MS_TICS; + } + + /** + * Return current native timer value + */ + unsigned value(unsigned const) + { + addr_t time = _stime(); + return time < _timeout ? _timeout - time : 0; + } + + static unsigned interrupt_id(int) { return 1; } +}; + +namespace Kernel { class Timer : public Genode::Timer { }; } + +#endif /* _TIMER_H_ */ diff --git a/repos/base-hw/src/core/include/spec/riscv/translation_table.h b/repos/base-hw/src/core/include/spec/riscv/translation_table.h new file mode 100644 index 000000000..130fe9dc4 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/riscv/translation_table.h @@ -0,0 +1,411 @@ +/** + * \brief RISCV Sv39 page table format + * \author Sebastian Sumpf + * \date 2015-08-04 + */ + +/* + * Copyright (C) 2015-2016 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 _TRANSLATION_TABLE_H_ +#define _TRANSLATION_TABLE_H_ + +#include +#include + +#include +#include + +namespace Sv39 +{ + using namespace Genode; + + enum { + SIZE_LOG2_4K = 12, + SIZE_LOG2_2M = 21, + SIZE_LOG2_1G = 30, + SIZE_LOG2_512G = 39, + }; + + struct None { }; + + template + class Level_x_translation_table; + + using Level_3_translation_table = + Level_x_translation_table; + + using Level_2_translation_table = + Level_x_translation_table; + + using Level_1_translation_table = + Level_x_translation_table; + + struct Descriptor; + struct Table_descriptor; + struct Block_descriptor; +} + +struct Sv39::Descriptor : Register<64> +{ + enum Descriptor_type { INVALID, TABLE, BLOCK }; + struct V : Bitfield<0, 1> { }; /* present */ + struct Type : Bitfield<1, 4> /* type and access rights */ + { + enum { + POINTER = 0, + POINTER_GLOBAL = 1, + USER = 4, /* R + 0, RW + 1, RX + 2, RWX + 3 */ + KERNEL = 8, + GLOBAL = 12, + }; + }; + struct Ppn : Bitfield<10, 38> { }; /* physical address 10 bit aligned */ + struct Base : Bitfield<12, 38> { }; /* physical address page aligned */ + + template + static access_t rwx(Page_flags const &f) + { + if (f.writeable && f.executable) + return BASE + 3; + else if (f.writeable) + return BASE + 1; + else if (f.executable) + return BASE + 2; + else + return BASE; + } + + static access_t permission_bits(Page_flags const &f) + { + if (f.global) + return rwx(f); + + if (f.privileged) + return rwx(f); + + return rwx(f); + } + + static Descriptor_type type(access_t const v) + { + if (!V::get(v)) return INVALID; + if (Type::get(v) == Type::POINTER || Type::get(v) == Type::POINTER_GLOBAL) + return TABLE; + + return BLOCK; + } + + static bool valid(access_t const v) { + return V::get(v); } +}; + +struct Sv39::Table_descriptor : Descriptor +{ + static access_t create(void * const pa) + { + access_t base = Base::get((access_t)pa); + access_t desc = 0; + + Ppn::set(desc, base); + Type::set(desc, Type::POINTER); + V::set(desc, 1); + + return desc; + } +}; + +struct Sv39::Block_descriptor : Descriptor +{ + static access_t create(Page_flags const &f, addr_t const pa) + { + access_t base = Base::get(pa); + access_t desc = 0; + + Ppn::set(desc, base); + Type::set(desc, permission_bits(f)); + V::set(desc, 1); + + return desc; + } +}; + +template +class Sv39::Level_x_translation_table +{ + private: + + bool _aligned(addr_t const a, size_t const alignm_log2) { + return a == ((a >> alignm_log2) << alignm_log2); } + + public: + + static constexpr size_t MIN_PAGE_SIZE_LOG2 = SIZE_LOG2_4K; + static constexpr size_t ALIGNM_LOG2 = SIZE_LOG2_4K; + static constexpr size_t MAX_ENTRIES = 1 << (SIZE_LOG2 - BLOCK_SIZE_LOG2); + static constexpr size_t BLOCK_SIZE = 1 << BLOCK_SIZE_LOG2; + static constexpr size_t BLOCK_MASK = ~(BLOCK_SIZE - 1); + static constexpr size_t VM_MASK = (1UL<< SIZE_LOG2_512G) - 1; + + class Misaligned { }; + class Invalid_range { }; + class Double_insertion { }; + + protected: + + typename Descriptor::access_t _entries[MAX_ENTRIES]; + + /* + * Return how many entries of an alignment fit into region + */ + static constexpr size_t _count(size_t region, size_t alignment) { + return align_addr(region, alignment) / (1UL << alignment); } + + + template + void _range_op(addr_t vo, addr_t pa, size_t size, FUNC &&func) + { + /* sanity check vo bits 38 to 63 must be equal */ + addr_t sanity = vo >> 38; + if (sanity != 0 && sanity != 0x3ffffff) { + PERR("Invalid virtual address: %lx", vo); + throw Invalid_range(); + } + + /* clear bits 39 - 63 */ + vo &= VM_MASK; + + for (size_t i = vo >> BLOCK_SIZE_LOG2; size > 0; + i = vo >> BLOCK_SIZE_LOG2) { + addr_t end = (vo + BLOCK_SIZE) & BLOCK_MASK; + size_t sz = min(size, end-vo); + + func(vo, pa, sz, _entries[i]); + + /* check whether we wrap */ + if (end < vo) return; + + size = size - sz; + vo += sz; + pa += sz; + } + } + + template + struct Insert_func + { + Page_flags const & flags; + Translation_table_allocator * alloc; + + Insert_func(Page_flags const & flags, + Translation_table_allocator * alloc) + : flags(flags), alloc(alloc) { } + + void operator () (addr_t const vo, + addr_t const pa, + size_t const size, + typename Descriptor::access_t &desc) + { + /* can we insert a whole block? */ + if (!((vo & ~BLOCK_MASK) || (pa & ~BLOCK_MASK) || size < BLOCK_SIZE)) { + typename Descriptor::access_t blk_desc = + Block_descriptor::create(flags, pa); + + if (Descriptor::valid(desc) && desc != blk_desc) + throw Double_insertion(); + + desc = blk_desc; + return; + } + + /* we need to use a next level table */ + ENTRY *table; + switch (Descriptor::type(desc)) { + + case Descriptor::INVALID: /* no entry */ + { + if (!alloc) + throw Allocator::Out_of_memory(); + + /* create and link next level table */ + table = new (alloc) ENTRY(); + ENTRY * phys_addr = (ENTRY*) alloc->phys_addr(table); + desc = Table_descriptor::create(phys_addr ? + phys_addr : table); + } + + case Descriptor::TABLE: /* table already available */ + { + /* use allocator to retrieve virt address of table */ + ENTRY * phys_addr = (ENTRY*) + Table_descriptor::Base::bits(Table_descriptor::Ppn::get(desc)); + table = (ENTRY*) alloc->virt_addr(phys_addr); + table = table ? table : (ENTRY*)phys_addr; + break; + } + + case Descriptor::BLOCK: /* there is already a block */ + throw Double_insertion(); + }; + + /* insert translation */ + table->insert_translation(vo - (vo & BLOCK_MASK), + pa, size, flags, alloc); + } + }; + + template + struct Remove_func + { + Translation_table_allocator * alloc; + + Remove_func(Translation_table_allocator * alloc) : alloc(alloc) { } + + void operator () (addr_t const vo, + addr_t const pa, + size_t const size, + typename Descriptor::access_t &desc) + { + switch (Descriptor::type(desc)) { + case Descriptor::TABLE: + { + /* use allocator to retrieve virt address of table */ + ENTRY * phys_addr = (ENTRY*) + Table_descriptor::Base::bits(Table_descriptor::Ppn::get(desc)); + ENTRY * table = (ENTRY*) alloc->virt_addr(phys_addr); + table = table ? table : (ENTRY*)phys_addr; + table->remove_translation(vo - (vo & BLOCK_MASK), + size, alloc); + if (!table->empty()) + break; + destroy(alloc, table); + } + case Descriptor::BLOCK: + case Descriptor::INVALID: + desc = 0; + } + } + }; + + public: + + Level_x_translation_table() + { + if (!_aligned((addr_t)this, ALIGNM_LOG2)) { + PWRN("misaligned address"); + throw Misaligned(); + } + + memset(&_entries, 0, sizeof(_entries)); + } + + bool empty() + { + for (unsigned i = 0; i < MAX_ENTRIES; i++) + if (Descriptor::valid(_entries[i])) + return false; + return true; + } + + /** + * Insert translations into this table + * + * \param vo offset of the virtual region represented + * by the translation within the virtual + * region represented by this table + * \param pa base of the physical backing store + * \param size size of the translated region + * \param flags mapping flags + * \param alloc level allocator + */ + void insert_translation(addr_t vo, addr_t pa, size_t size, + Page_flags const & flags, + Translation_table_allocator * alloc ) + { + _range_op(vo, pa, size, Insert_func(flags, alloc)); + } + + /** + * Remove translations that overlap with a given virtual region + * + * \param vo region offset within the tables virtual region + * \param size region size + * \param alloc level allocator + */ + void remove_translation(addr_t vo, size_t size, + Translation_table_allocator * alloc) + { + _range_op(vo, 0, size, Remove_func(alloc)); + } +} __attribute__((aligned(1 << ALIGNM_LOG2))); + +namespace Sv39 { + + /** + * Insert/Remove functor specialization for level 3 + */ + template <> template <> + struct Level_3_translation_table::Insert_func + { + Page_flags const & flags; + Translation_table_allocator * alloc; + + Insert_func(Page_flags const & flags, + Translation_table_allocator * alloc) + : flags(flags), alloc(alloc) { } + + void operator () (addr_t const vo, + addr_t const pa, + size_t const size, + Descriptor::access_t &desc) + { + if ((vo & ~BLOCK_MASK) || (pa & ~BLOCK_MASK) || + size < BLOCK_SIZE) { + PWRN("invalid range"); + throw Invalid_range(); + } + + Descriptor::access_t blk_desc = + Block_descriptor::create(flags, pa); + + if (Descriptor::valid(desc) && desc != blk_desc) + throw Double_insertion(); + + desc = blk_desc; + } + }; + + template <> template <> + struct Level_3_translation_table::Remove_func + { + Remove_func(Translation_table_allocator * /* alloc */) { } + + void operator () (addr_t const vo, + addr_t const pa, + size_t const size, + Descriptor::access_t &desc) { + desc = 0; } + }; +} + +namespace Genode { + + class Translation_table : public Sv39::Level_1_translation_table + { + public: + + enum { + TABLE_LEVEL_X_SIZE_LOG2 = Sv39::SIZE_LOG2_4K, + CORE_VM_AREA_SIZE = 128 * 1024 * 1024, + CORE_TRANS_TABLE_COUNT = + _count(CORE_VM_AREA_SIZE, Sv39::SIZE_LOG2_1G) + + _count(CORE_VM_AREA_SIZE, Sv39::SIZE_LOG2_2M), + }; + }; +} /* namespace Genode */ + +#endif /* _TRANSLATION_TABLE_H_ */ diff --git a/repos/base-hw/src/core/kernel/pd.cc b/repos/base-hw/src/core/kernel/pd.cc index 35821d327..c9295a4c3 100644 --- a/repos/base-hw/src/core/kernel/pd.cc +++ b/repos/base-hw/src/core/kernel/pd.cc @@ -14,6 +14,7 @@ /* core includes */ #include +#include /* Genode includes */ #include @@ -56,7 +57,7 @@ void Mode_transition_control::map(Genode::Translation_table * tt, { try { addr_t const phys_base = (addr_t)&_mt_begin; - tt->insert_translation(VIRT_BASE, phys_base, SIZE, + tt->insert_translation(Genode::trunc_page(VIRT_BASE), phys_base, SIZE, Genode::Page_flags::mode_transition(), alloc); } catch(...) { PERR("Inserting exception vector in page table failed!"); } diff --git a/repos/base-hw/src/core/platform_pd.cc b/repos/base-hw/src/core/platform_pd.cc index 844bcb822..ee3a6eb1d 100644 --- a/repos/base-hw/src/core/platform_pd.cc +++ b/repos/base-hw/src/core/platform_pd.cc @@ -2,6 +2,7 @@ * \brief Protection-domain facility * \author Martin Stein * \author Stefan Kalkowski + * \author Sebastian Sumpf * \date 2012-02-12 */ @@ -210,9 +211,16 @@ void Core_platform_pd::_map(addr_t start, addr_t end, bool io_mem) const Page_flags flags = Page_flags::apply_mapping(true, io_mem ? UNCACHED : CACHED, io_mem); - start = trunc_page(start); - size_t size = round_page(end) - start; + start = trunc_page(start); + /* omitt regions before vm_start */ + if (start < VIRT_ADDR_SPACE_START) + start = VIRT_ADDR_SPACE_START; + + if (end > VIRT_ADDR_SPACE_START + VIRT_ADDR_SPACE_SIZE) + end = VIRT_ADDR_SPACE_START + VIRT_ADDR_SPACE_SIZE; + + size_t size = round_page(end) - start; try { _table()->insert_translation(start, start, size, flags, _table_alloc()); } catch(Allocator::Out_of_memory) { diff --git a/repos/base-hw/src/core/spec/riscv/crt0.s b/repos/base-hw/src/core/spec/riscv/crt0.s new file mode 100644 index 000000000..1887099e1 --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/crt0.s @@ -0,0 +1,27 @@ +/** + * \brief Startup code for the core's userland part + * \author Sebastian Sumpf + * \date 2015-09-12 + */ + +/* + * Copyright (C) 2015-2016 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. + */ + +.section ".text" + +.global _core_start +_core_start: + + /* create environment for main thread */ + jal init_main_thread + + /* load stack pointer from init_main_thread_result */ + la sp, init_main_thread_result + ld sp, (sp) + + /* jump into init C-code */ + j _main diff --git a/repos/base-hw/src/core/spec/riscv/kernel/cpu.cc b/repos/base-hw/src/core/spec/riscv/kernel/cpu.cc new file mode 100644 index 000000000..36aafad16 --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/cpu.cc @@ -0,0 +1,86 @@ +/* + * \brief Class for kernel data that is needed to manage a specific CPU + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 + +using namespace Kernel; + +extern Genode::addr_t _mt_client_context_ptr; + +struct Mstatus : Genode::Register<64> +{ + enum { + USER = 0, + SUPERVISOR = 1, + MACHINE = 3, + Sv39 = 9, + }; + struct Ie : Bitfield<0, 1> { }; + struct Priv : Bitfield<1, 2> { }; + struct Ie1 : Bitfield<3, 1> { }; + struct Priv1 : Bitfield<4, 2> { }; + struct Fs : Bitfield<12, 2> { enum { INITIAL = 1 }; }; + struct Vm : Bitfield<17, 5> { }; + struct Mprv : Bitfield<16, 1> { }; +}; + + +void Kernel::Cpu::init(Kernel::Pic &pic, Kernel::Pd & core_pd, Genode::Board & board) +{ + /* read status register */ + Mstatus::access_t mstatus = 0; + + Mstatus::Vm::set(mstatus, Mstatus::Sv39); /* enable Sv39 paging */ + Mstatus::Fs::set(mstatus, Mstatus::Fs::INITIAL); /* enable FPU */ + Mstatus::Ie1::set(mstatus, 1); /* user mode interrupt */ + Mstatus::Priv1::set(mstatus, Mstatus::USER); /* set user mode */ + Mstatus::Ie::set(mstatus, 0); /* disable interrupts */ + Mstatus::Priv::set(mstatus, Mstatus::SUPERVISOR); /* set supervisor mode */ + + asm volatile ("csrw sasid, %0\n" /* address space id */ + "csrw sptbr, %1\n" /* set page table */ + "csrw mstatus, %2\n" /* change mode */ + "csrw stvec, %3\n" /* exception vector */ + "csrw sscratch,%4\n" /* master conext ptr */ + : + : "r" (core_pd.asid), + "r" (core_pd.translation_table()), + "r" (mstatus), + "r" (exception_entry), + "r" (exception_entry | ((addr_t)&_mt_client_context_ptr & 0xfff)) + : "memory"); +} + + +Cpu_idle::Cpu_idle(Cpu * const cpu) : Cpu_job(Cpu_priority::MIN, 0) +{ + Cpu_job::cpu(cpu); + cpu_exception = RESET; + ip = (addr_t)&_main; + sp = (addr_t)&_stack[stack_size]; + init_thread((addr_t)core_pd()->translation_table(), core_pd()->asid); +} + + +void Cpu_idle::exception(unsigned const cpu) +{ + if (is_irq()) { + _interrupt(cpu); + return; + } else if (cpu_exception == RESET) return; + + assert(0); +} diff --git a/repos/base-hw/src/core/spec/riscv/kernel/cpu_context.cc b/repos/base-hw/src/core/spec/riscv/kernel/cpu_context.cc new file mode 100644 index 000000000..f60c6146f --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/cpu_context.cc @@ -0,0 +1,26 @@ +/** + * \brief Kernel cpu context specific implementation + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 + +void Kernel::Cpu_context::_init(size_t const stack_size, addr_t const table) +{ + /* + * The stack pointer currently contains the base address of the + * kernel stack area that contains the kernel stacks of all CPUs. As this + * is a uni-processor platform, we merely have to select the first kernel + * stack, i.e. increasing sp by the size of one stack. + */ + sp = sp + stack_size; +} diff --git a/repos/base-hw/src/core/spec/riscv/kernel/crt0.s b/repos/base-hw/src/core/spec/riscv/kernel/crt0.s new file mode 100644 index 000000000..18fc325d4 --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/crt0.s @@ -0,0 +1,44 @@ +/** + * \brief Kernel startup code + * \author Sebastian Sumpf + * \date 2015-06-010 + */ + +/* + * Copyright (C) 2015-2016 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. + */ + +.section ".text.crt0" + +.global _start +_start: +j _start_next_page + +/* leave first page empty for mode-transition page located at 0x100 */ +.space 4096 + +_start_next_page: + + /* clear the bss segment */ +la a0, _bss_start +la a1, _bss_end +1: +sd x0, (a0) +addi a0, a0, 8 +bne a0, a1, 1b + +la sp, kernel_stack +la a0, kernel_stack_size +ld a0, (a0) +add sp, sp, a0 + +/* save kernel stack pointer in mscratch */ +csrw mscratch, sp + +jal setup_riscv_exception_vector +jal init_kernel + +1: j 1b diff --git a/repos/base-hw/src/core/spec/riscv/kernel/exception_vector.cc b/repos/base-hw/src/core/spec/riscv/kernel/exception_vector.cc new file mode 100644 index 000000000..855ac75af --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/exception_vector.cc @@ -0,0 +1,29 @@ +/* + * \brief Exception vector initialization + * \author Sebastian Sumpf + * \date 2015-07-12 + */ + +/* + * Copyright (C) 2015-2016 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 + +extern int _machine_begin, _machine_end; + +extern "C" void setup_riscv_exception_vector() +{ + using namespace Genode; + + /* retrieve exception vector */ + addr_t vector; + asm volatile ("csrr %0, mtvec\n" : "=r"(vector)); + + /* copy machine mode exception vector */ + memcpy((void *)vector, + &_machine_begin, (addr_t)&_machine_end - (addr_t)&_machine_begin); +} diff --git a/repos/base-hw/src/core/spec/riscv/kernel/pd.cc b/repos/base-hw/src/core/spec/riscv/kernel/pd.cc new file mode 100644 index 000000000..95e0f9d9f --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/pd.cc @@ -0,0 +1,50 @@ +/* + * \brief Kernel backend for protection domains + * \author Stefan Kalkowski + * \date 2015-03-20 + */ + +/* + * Copyright (C) 2015-2016 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 + +using Asid_allocator = Genode::Bit_allocator<256>; + +static Asid_allocator &alloc() { + return *unmanaged_singleton(); } + + +Kernel::Pd::Pd(Kernel::Pd::Table * const table, + Genode::Platform_pd * const platform_pd) +: Kernel::Cpu::Pd((Genode::uint8_t)alloc().alloc()), + _table(table), _platform_pd(platform_pd) +{ + capid_t invalid = _capid_alloc.alloc(); + assert(invalid == cap_id_invalid()); +} + + +Kernel::Pd::~Pd() +{ + while (Object_identity_reference *oir = _cap_tree.first()) + oir->~Object_identity_reference(); + + /* clean up buffers of memory management */ + Cpu::flush_tlb_by_pid(asid); + alloc().free(asid); +} + + +void Kernel::Pd::admit(Kernel::Cpu::Context * const c) +{ + c->protection_domain(asid); + c->translation_table((addr_t)translation_table()); +} diff --git a/repos/base-hw/src/core/spec/riscv/kernel/thread.cc b/repos/base-hw/src/core/spec/riscv/kernel/thread.cc new file mode 100644 index 000000000..4c9633856 --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/thread.cc @@ -0,0 +1,71 @@ +/* + * \brief Kernel backend for execution contexts in userland + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 + +using namespace Kernel; + +void Kernel::Thread::_init() { } + +void Thread::exception(unsigned const cpu) +{ + if (is_irq()) + return; + + switch(cpu_exception) { + case SUPERVISOR_CALL: + _call(); + ip += 4; /* set to next instruction */ + break; + case INSTRUCTION_PAGE_FAULT: + case STORE_PAGE_FAULT: + case LOAD_PAGE_FAULT: + _mmu_exception(); + break; + default: + PWRN("%s -> %s: unhandled exception %lu at ip=%lx", + pd_label(), label(), cpu_exception, ip); + _stop(); + } +} + + +void Thread::_mmu_exception() +{ + _become_inactive(AWAITS_RESUME); + _fault_pd = (addr_t)_pd->platform_pd(); + _fault_signal = (addr_t)_fault.signal_context(); + _fault_addr = Cpu::sbadaddr(); + + _fault.submit(); +} + + +void Thread::_call_update_pd() +{ + asm volatile ("sfence.vm"); +} + + +void Thread::_call_update_data_region() { } + + +void Thread::_call_update_instr_region() { } + + +void Thread_event::_signal_acknowledged() +{ + _thread->_resume(); +} diff --git a/repos/base-hw/src/core/spec/riscv/kernel/thread_base.cc b/repos/base-hw/src/core/spec/riscv/kernel/thread_base.cc new file mode 100644 index 000000000..b17d7a2e3 --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/kernel/thread_base.cc @@ -0,0 +1,65 @@ +/* + * \brief CPU specific implementations of core + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 + +using namespace Kernel; + + +/************************* + ** Kernel::Thread_base ** + *************************/ + +Thread_base::Thread_base(Thread * const t) +: + _fault(t), + _fault_pd(0), + _fault_addr(0), + _fault_writes(0), + _fault_signal(0) +{ } + + +/************************* + ** Kernel::Cpu_context ** + *************************/ + +void Kernel::Cpu_context::_init(size_t const stack_size, addr_t const table) +{ + /* + * the stack pointer already contains the stack base address + * of all CPU's kernel stacks, on this uni-processor platform + * it is sufficient to increase it by the stack's size + */ + sp = sp + stack_size; +} + + +/************************* + ** CPU-state utilities ** + *************************/ + +typedef Thread_reg_id Reg_id; + +static addr_t const _cpu_state_regs[] = { }; + +addr_t const * cpu_state_regs() { return _cpu_state_regs; } + + +size_t cpu_state_regs_length() +{ + return sizeof(_cpu_state_regs)/sizeof(_cpu_state_regs[0]); +} diff --git a/repos/base-hw/src/core/spec/riscv/mode_transition.s b/repos/base-hw/src/core/spec/riscv/mode_transition.s new file mode 100644 index 000000000..fb90ec49c --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/mode_transition.s @@ -0,0 +1,268 @@ +/* + * \brief Transition between kernel/userland + * \date 2011-11-15 + */ + +/* + * Copyright (C) 2011-2015 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. + */ +.set USER_MODE, 0 +.set SUPERVISOR_MODE, 1 +.set MACHINE_MODE, 3 + +.set CALL_PUT_CHAR, 0xff + +.set CPU_IP, 0 +.set CPU_EXCEPTION, 8 +.set CPU_X1, 2*8 +.set CPU_SP, 3*8 +.set CPU_SASID, 33*8 +.set CPU_SPTBR, 34*8 + +.macro _save_scratch_registers mode + + .if \mode == USER_MODE + csrrw sp, mscratch, sp + .endif + + addi sp, sp, -24 + sd t0, 0(sp) + sd t1, 8(sp) + sd t2, 16(sp) +.endm + +.macro _restore_scratch_registers mode + ld t0, 0(sp) + ld t1, 8(sp) + ld t2, 16(sp) + addi sp, sp, 24 + + .if \mode == USER_MODE + csrrw sp, mscratch, sp + .endif +.endm + +.macro _put_char mode + + /* check if ecall (8 - 11) */ + csrr t0, mcause + li t1, 8 + bltu t0, t1, 9f + li t1, 12 + bgtu t0, t1, 9f + + /* check for put char ecall number */ + li t1, CALL_PUT_CHAR + bne t1, a0, 9f + + /* output character */ + csrw mtohost, a1 + +1: + li t0, 0 + csrrw t0, mfromhost, t0 + beqz t0, 1b + + /* advance epc */ + csrr t0, mepc + addi t0, t0, 4 + csrw mepc, t0 + + _restore_scratch_registers \mode + eret +9: +.endm + +.section .text + +/* + * Page aligned base of mode transition code. + * + * This position independent code switches between a kernel context and a + * user context and thereby between their address spaces. Due to the latter + * it must be mapped executable to the same region in every address space. + * To enable such switching, the kernel context must be stored within this + * region, thus one should map it solely accessable for privileged modes. + */ + + + +.p2align 8 +.global _machine_begin +_machine_begin: + +/* 0x100 user mode */ +j user_trap +.space 0x3c +/* 0x140 supervisor */ +j supervisor_trap +.space 0x3c +/* 0x180 hypervisor */ +1: j 1b +.space 0x3c +/* 0x1c0 machine */ +j machine_trap +.space 0x38 +/* 0x1fc non-maksable interrupt */ +1: j 1b + +user_trap: + + _save_scratch_registers USER_MODE + _put_char USER_MODE + _restore_scratch_registers USER_MODE + mrts + +supervisor_trap: + + _save_scratch_registers SUPERVISOR_MODE + _put_char SUPERVISOR_MODE + j fault + +machine_trap: + + _save_scratch_registers MACHINE_MODE + _put_char MACHINE_MODE + j fault + + +fault:j fault /* TODO: handle trap from supervisor or machine mode */ + +.global _machine_end +_machine_end: + +.p2align 12 +.global _mt_begin +_mt_begin: + +/* 0x100 user mode */ + j _mt_kernel_entry_pic +.space 0x3c +/* 0x140 supervisor */ +1: j 1b +.space 0x3c +/* 0x180 hypervisor */ +1: j 1b +.space 0x3c +/* 0x1c0 machine */ +1: j 1b +.space 0x38 +/* 0x1fc non-maksable interrupt */ +1: j 1b + +/* space for a client context-pointer per CPU */ +.p2align 2 +.global _mt_client_context_ptr +_mt_client_context_ptr: +.space 8 + +/* space for a copy of the kernel context */ +.global _mt_master_context_begin +_mt_master_context_begin: + +/* space must be at least as large as 'Context' */ +.space 35*8 + +.global _mt_master_context_end +_mt_master_context_end: + +.global _mt_kernel_entry_pic +_mt_kernel_entry_pic: + + /* master context */ + csrrw x31, sscratch, x31 + addi x31, x31, 8 + + /* save x29, x30 in master */ + sd x29, CPU_X1 + 8 * 28(x31) + sd x30, CPU_X1 + 8 * 29(x31) + + /* load kernel page table */ + ld x29, CPU_SASID(x31) + ld x30, CPU_SPTBR(x31) + + csrw sasid, x29 + csrw sptbr, x30 + + /* save x29 - x31 in user context */ + mv x29, x31 + addi x29, x29, -8 + ld x29, (x29) + + .irp reg,29,30 + ld x30, CPU_X1 + 8 * (\reg - 1)(x31) + sd x30, CPU_X1 + 8 * (\reg - 1)(x29) + .endr + + csrr x30, sscratch /* x31 */ + sd x30, CPU_X1 + 8 * 30(x29) + + /* save x1 - x28 */ + .irp reg,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28 + sd x\reg, CPU_X1 + 8 * (\reg - 1)(x29) + .endr + + /* trap reason */ + csrr x30, scause + sd x30, CPU_EXCEPTION(x29) + + /* ip */ + csrr x30, sepc + sd x30, CPU_IP(x29) + + /* load kernel stack and ip */ + ld sp, CPU_SP(x31) + ld x30, CPU_IP(x31) + + /* restore scratch */ + addi x31, x31, -8 + csrw sscratch, x31 + + jalr x30 + + +.global _mt_user_entry_pic +_mt_user_entry_pic: + + /* client context pointer */ + csrr x30, sscratch + ld x30, (x30) + + /* set return IP */ + ld x31, CPU_IP(x30) + csrw sepc, x31 + + /* restore x1-x28 */ + .irp reg,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28 + ld x\reg, CPU_X1 + 8 * (\reg - 1)(x30) + .endr + + /* save x29, x30, x31 to master context */ + csrr x29, sscratch + addi x29, x29, 8 /* master context */ + + .irp reg,29,30,31 + ld x31, CPU_X1 + 8 * (\reg - 1)(x30) + sd x31, CPU_X1 + 8 * (\reg - 1)(x29) + .endr + + /* switch page table */ + ld x31, CPU_SASID(x30) + ld x30, CPU_SPTBR(x30) + + csrw sasid, x31 + csrw sptbr, x30 + + /* restore x29 - x31 from master context */ + .irp reg,31,30,29 + ld x\reg, CPU_X1 + 8 * (\reg - 1)(x29) + .endr + + eret + +/* end of the mode transition code */ +.global _mt_end +_mt_end: diff --git a/repos/base-hw/src/core/spec/riscv/platform_support.cc b/repos/base-hw/src/core/spec/riscv/platform_support.cc new file mode 100644 index 000000000..09a18090a --- /dev/null +++ b/repos/base-hw/src/core/spec/riscv/platform_support.cc @@ -0,0 +1,57 @@ +/* + * \brief Platform implementations specific for RISC-V + * \author Sebastian Sumpf + * \date 2015-06-02 + */ + +/* + * Copyright (C) 2015-2016 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 + +using namespace Genode; + +Cpu::User_context::User_context() { } + + +Native_region * Platform::_ram_regions(unsigned const i) +{ + static Native_region _regions[] = + { + { 0, 128 * 1024 * 1024 } + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +Native_region * Platform::_core_only_mmio_regions(unsigned const i) +{ + static Native_region _regions[] = + { + }; + return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; +} + + +void Platform::_init_io_port_alloc() +{ } + + +void Platform::_init_io_mem_alloc() +{ } + +void Platform::setup_irq_mode(unsigned, unsigned, unsigned) { PDBG("not impl");} + + +long Platform::irq(long const user_irq) +{ + PDBG("not impl"); + return 0; +} diff --git a/repos/base-hw/src/core/target.mk b/repos/base-hw/src/core/target.mk index b0817a814..fc36051d1 100644 --- a/repos/base-hw/src/core/target.mk +++ b/repos/base-hw/src/core/target.mk @@ -1,6 +1,7 @@ # # \brief Build config for Genodes core process # \author Martin Stein +# \author Sebastian Sumpf # \date 2011-12-16 # @@ -12,3 +13,7 @@ LIBS += core # add C++ sources SRC_CC += kernel/test.cc + +ifneq ($(filter riscv, $(SPECS)),) +LD_TEXT_ADDR = $(CORE_LD_TEXT_ADDR) +endif diff --git a/repos/base-hw/src/lib/startup/spec/riscv/crt0.s b/repos/base-hw/src/lib/startup/spec/riscv/crt0.s new file mode 100644 index 000000000..ab5915b61 --- /dev/null +++ b/repos/base-hw/src/lib/startup/spec/riscv/crt0.s @@ -0,0 +1,40 @@ +/** + * \brief Startup code for Genode applications + * \author Sebastian Sumpf + * \date 2016-02-16 + */ + +/* + * Copyright (C) 2016 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. + */ + +.section ".text" + +.global _start +_start: + + /* load stack pointer */ + lla sp, _stack_high + + /* relocate linker */ + jal init_rtld + + /* create environment for main thread */ + jal init_main_thread + + /* load stack pointer from init_main_thread_result */ + la sp, init_main_thread_result + ld sp, (sp) + + mv s0, x0 + + /* jump into init C-code */ + j _main + +.bss + .p2align 8 + .space 32*1024 + _stack_high: