diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index cd979eb25..ea3442e7e 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/libmesosphere/include/mesosphere.hpp @@ -37,9 +37,13 @@ #include "mesosphere/kern_k_memory_layout.hpp" /* Core functionality. */ -#include "mesosphere/kern_select_interrupts.hpp" +#include "mesosphere/kern_select_interrupt_manager.hpp" +#include "mesosphere/kern_k_spin_lock.hpp" #include "mesosphere/kern_k_page_heap.hpp" #include "mesosphere/kern_k_memory_manager.hpp" +#include "mesosphere/kern_k_interrupt_task_manager.hpp" +#include "mesosphere/kern_k_core_local_region.hpp" +#include "mesosphere/kern_kernel.hpp" /* Supervisor Calls. */ #include "mesosphere/kern_svc.hpp" diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp new file mode 100644 index 000000000..6f912bf22 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_hardware_timer.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern::arm64 { + + class KHardwareTimer : public KHardwareTimerBase { + public: + static constexpr s32 InterruptId = 30; /* Nintendo uses the non-secure timer interrupt. */ + public: + constexpr KHardwareTimer() : KHardwareTimerBase() { /* ... */ } + + virtual void DoTask() override; + + /* TODO: Actually implement more of KHardwareTimer, */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp new file mode 100644 index 000000000..e76df1cf3 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::kern::arm64 { + + struct GicDistributor { + u32 ctlr; + u32 typer; + u32 iidr; + u32 reserved_0x0c; + u32 statusr; + u32 reserved_0x14[3]; + u32 impldef_0x20[8]; + u32 setspi_nsr; + u32 reserved_0x44; + u32 clrspi_nsr; + u32 reserved_0x4c; + u32 setspi_sr; + u32 reserved_0x54; + u32 clrspi_sr; + u32 reserved_0x5c[9]; + u32 igroupr[32]; + u32 isenabler[32]; + u32 icenabler[32]; + u32 ispendr[32]; + u32 icpendr[32]; + u32 isactiver[32]; + u32 icactiver[32]; + u8 ipriorityr[1020]; + u32 _0x7fc; + u8 itargetsr[1020]; + u32 _0xbfc; + u32 icfgr[64]; + u32 igrpmodr[32]; + u32 _0xd80[32]; + u32 nsacr[64]; + u32 sgir; + u32 _0xf04[3]; + u32 cpendsgir[4]; + u32 spendsgir[4]; + u32 reserved_0xf30[52]; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(GicDistributor) == 0x1000); + + struct GicController { + u32 ctlr; + u32 pmr; + u32 bpr; + u32 iar; + u32 eoir; + u32 rpr; + u32 hppir; + u32 abpr; + u32 aiar; + u32 aeoir; + u32 ahppir; + u32 statusr; + u32 reserved_30[4]; + u32 impldef_40[36]; + u32 apr[4]; + u32 nsapr[4]; + u32 reserved_f0[3]; + u32 iidr; + u32 reserved_100[960]; + u32 dir; + u32 _0x1004[1023]; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(GicController) == 0x2000); + + struct KInterruptController { + NON_COPYABLE(KInterruptController); + NON_MOVEABLE(KInterruptController); + public: + static constexpr size_t NumLocalInterrupts = 32; + static constexpr size_t NumGlobalInterrupts = 988; + static constexpr size_t NumInterrupts = NumLocalInterrupts + NumGlobalInterrupts; + public: + struct LocalState { + u32 local_isenabler[NumLocalInterrupts / 32]; + u32 local_ipriorityr[NumLocalInterrupts / 4]; + u32 local_targetsr[NumLocalInterrupts / 4]; + u32 local_icfgr[NumLocalInterrupts / 16]; + }; + + struct GlobalState { + u32 global_isenabler[NumGlobalInterrupts / 32]; + u32 global_ipriorityr[NumGlobalInterrupts / 4]; + u32 global_targetsr[NumGlobalInterrupts / 4]; + u32 global_icfgr[NumGlobalInterrupts / 16]; + }; + private: + static inline volatile GicDistributor *s_gicd; + static inline volatile GicController *s_gicc; + static inline u32 s_mask[cpu::NumCores]; + private: + volatile GicDistributor *gicd; + volatile GicController *gicc; + public: + KInterruptController() { /* Don't initialize anything -- this will be taken care of by ::Initialize() */ } + + /* TODO: Actually implement KInterruptController functionality. */ + }; +} diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp new file mode 100644 index 000000000..132e24eee --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_manager.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::kern::arm64 { + + class KInterruptManager { + NON_COPYABLE(KInterruptManager); + NON_MOVEABLE(KInterruptManager); + private: + struct KCoreLocalInterruptEntry { + KInterruptHandler *handler; + bool manually_cleared; + bool needs_clear; + u8 priority; + }; + + struct KGlobalInterruptEntry { + KInterruptHandler *handler; + bool manually_cleared; + bool needs_clear; + }; + private: + static inline KSpinLock s_lock; + static inline KGlobalInterruptEntry s_global_interrupts[KInterruptController::NumGlobalInterrupts]; + static inline KInterruptController::GlobalState s_global_state; + static inline bool s_global_state_saved; + private: + KCoreLocalInterruptEntry core_local_interrupts[KInterruptController::NumLocalInterrupts]; + KInterruptController interrupt_controller; + KInterruptController::LocalState local_state; + bool local_state_saved; + public: + KInterruptManager() : local_state_saved(false) { /* Leave things mostly uninitalized. We'll call ::Initialize() later. */ } + /* TODO: Actually implement KInterruptManager functionality. */ + public: + static ALWAYS_INLINE u32 DisableInterrupts() { + u64 intr_state; + __asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state)); + __asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"(intr_state | 0x80)); + return intr_state; + } + + static ALWAYS_INLINE u32 EnableInterrupts() { + u64 intr_state; + __asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state)); + __asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"(intr_state & 0x7F)); + return intr_state; + } + + static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) { + u64 cur_state; + __asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state)); + __asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & 0x7F) | (intr_state & 0x80))); + } + + static ALWAYS_INLINE bool AreInterruptsEnabled() { + u64 intr_state; + __asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state)); + return (intr_state & 0x80) == 0; + } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_spin_lock.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_spin_lock.hpp new file mode 100644 index 000000000..adf87720e --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_spin_lock.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::kern::arm64 { + + class KNotAlignedSpinLock { + private: + u32 packed_tickets; + public: + constexpr KNotAlignedSpinLock() : packed_tickets(0) { /* ... */ } + + void Lock() { + u32 tmp0, tmp1; + + __asm__ __volatile__( + " prfm pstl1keep, %[packed_tickets]\n" + "loop1:\n" + " ldaxr %w[tmp0], %[packed_tickets]\n" + " add %w[tmp0], %w[tmp0], #0x10000\n" + " stxr %w[tmp1], %w[tmp0], %[packed_tickets]\n" + " cbnz %w[tmp1], loop1\n" + " \n" + " and %w[tmp1], %w[tmp0], #0xFFFF\n" + " cmp %w[tmp1], %w[tmp0], lsr #16\n" + " b.eq done" + " sevl\n" + "loop2:\n" + " wfe\n" + " ldaxrh %w[tmp1], %[packed_tickets]\n" + " cmp %w[tmp1], %w[tmp0], lsr #16\n" + " b.ne loop2\n" + "done:\n" + : [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [packed_tickets]"+Q"(this->packed_tickets) + : + : "cc", "memory" + ); + } + + void Unlock() { + const u32 value = this->packed_tickets + 1; + __asm__ __volatile__( + " stlrh %w[value], %[packed_tickets]\n" + : [packed_tickets]"+Q"(this->packed_tickets) + : [value]"r"(value) + : "memory" + ); + } + }; + static_assert(sizeof(KNotAlignedSpinLock) == sizeof(u32)); + + class KAlignedSpinLock { + private: + alignas(cpu::DataCacheLineSize) u16 current_ticket; + alignas(cpu::DataCacheLineSize) u16 next_ticket; + public: + constexpr KAlignedSpinLock() : current_ticket(0), next_ticket(0) { /* ... */ } + + void Lock() { + u32 tmp0, tmp1, got_lock; + + __asm__ __volatile__( + " prfm pstl1keep, %[next_ticket]\n" + "loop1:\n" + " ldaxrh %w[tmp0], %[next_ticket]\n" + " add %w[tmp1], %w[tmp0], #0x1\n" + " stxrh %w[got_lock], %w[tmp1], %[next_ticket]\n" + " cbnz %w[got_lock], loop1\n" + " \n" + " sevl\n" + "loop2:\n" + " wfe\n" + " ldaxrh %w[tmp1], %[current_ticket]\n" + " cmp %w[tmp1], %w[tmp0]\n" + " b.ne loop2\n" + : [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [got_lock]"=&r"(got_lock), [next_ticket]"+Q"(this->next_ticket) + : [current_ticket]"Q"(this->current_ticket) + : "cc", "memory" + ); + } + + void Unlock() { + const u32 value = this->current_ticket + 1; + __asm__ __volatile__( + " stlrh %w[value], %[current_ticket]\n" + : [current_ticket]"+Q"(this->current_ticket) + : [value]"r"(value) + : "memory" + ); + } + }; + static_assert(sizeof(KAlignedSpinLock) == 2 * cpu::DataCacheLineSize); + + using KSpinLock = KAlignedSpinLock; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_core_local_region.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_core_local_region.hpp new file mode 100644 index 000000000..4422df408 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_core_local_region.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ams::kern { + + struct KCoreLocalContext { + KCurrentContext current; + KScheduler scheduler; + KInterruptTaskManager interrupt_task_manager; + KInterruptManager interrupt_manager; + KHardwareTimer hardware_timer; + /* Everything after this point is for debugging. */ + /* Retail kernel doesn't even consistently update these fields. */ + u64 num_sw_interrupts; + u64 num_hw_interrupts; + std::atomic num_svc; + u64 num_process_switches; + u64 num_thread_switches; + u64 num_fpu_switches; + u64 num_scheduler_updates; + u64 num_invoked_scheduler_updates; + std::atomic num_specific_svc[0x80]; + u32 perf_counters[6]; + }; + static_assert(sizeof(KCoreLocalContext) < KMemoryManager::PageSize); + + struct KCoreLocalPage { + KCoreLocalContext context; + u8 padding[KMemoryManager::PageSize - sizeof(KCoreLocalContext)]; + }; + static_assert(sizeof(KCoreLocalPage) == KMemoryManager::PageSize); + + struct KCoreLocalRegion { + KCoreLocalPage current; + KCoreLocalPage absolute[cpu::NumCores]; + }; + static_assert(sizeof(KCoreLocalRegion) == KMemoryManager::PageSize * (1 + cpu::NumCores)); + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp new file mode 100644 index 000000000..1cf1abb69 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::kern { + + class KThread; + class KProcess; + class KScheduler; + class KInterruptTaskManager; + + struct KCurrentContext { + KThread *current_thread; + KProcess *current_process; + KScheduler *scheduler; + KInterruptTaskManager *interrupt_task_manager; + s32 core_id; + void *exception_stack_bottom; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(KCurrentContext) <= cpu::DataCacheLineSize); + + namespace impl { + + ALWAYS_INLINE KCurrentContext &GetCurrentContext() { + return *reinterpret_cast(cpu::GetCoreLocalRegionAddress()); + } + + } + + ALWAYS_INLINE KThread *GetCurrentThreadPointer() { + return impl::GetCurrentContext().current_thread; + } + + ALWAYS_INLINE KThread &GetCurrentThread() { + return *GetCurrentThreadPointer(); + } + + ALWAYS_INLINE KProcess *GetCurrentProcessPointer() { + return impl::GetCurrentContext().current_process; + } + + ALWAYS_INLINE KProcess &GetCurrentProcess() { + return *GetCurrentProcessPointer(); + } + + ALWAYS_INLINE KScheduler *GetCurrentSchedulerPointer() { + return impl::GetCurrentContext().scheduler; + } + + ALWAYS_INLINE KScheduler &GetCurrentScheduler() { + return *GetCurrentSchedulerPointer(); + } + + ALWAYS_INLINE KInterruptTaskManager *GetCurrentInterruptTaskManagerPointer() { + return impl::GetCurrentContext().interrupt_task_manager; + } + + ALWAYS_INLINE KInterruptTaskManager &GetCurrentInterruptTaskManager() { + return *GetCurrentInterruptTaskManagerPointer(); + } + + ALWAYS_INLINE s32 GetCurrentCoreId() { + return impl::GetCurrentContext().core_id; + } + + ALWAYS_INLINE void SetCurrentThread(KThread *new_thread) { + impl::GetCurrentContext().current_thread = new_thread; + } + + ALWAYS_INLINE void SetCurrentProcess(KProcess *new_process) { + impl::GetCurrentContext().current_process = new_process; + } + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_hardware_timer_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_hardware_timer_base.hpp new file mode 100644 index 000000000..c03d372ca --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_hardware_timer_base.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::kern { + + class KHardwareTimerBase : public KInterruptTask { + private: + using TimerTaskTree = util::IntrusiveRedBlackTreeBaseTraits::TreeType; + private: + KSpinLock lock; + TimerTaskTree task_tree; + KTimerTask *next_task; + public: + constexpr KHardwareTimerBase() : lock(), task_tree(), next_task(nullptr) { /* ... */ } + + virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { return this; } + protected: + KSpinLock &GetLock() { return this->lock; } + + /* TODO: Actually implement more of KHardwareTimerBase */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task.hpp new file mode 100644 index 000000000..642fa3203 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +namespace ams::kern { + + class KInterruptTask; + + class KInterruptHandler { + public: + virtual KInterruptTask *OnInterrupt(s32 interrupt_id) = 0; + }; + + class KInterruptTask : public KInterruptHandler { + private: + KInterruptTask *next_task; + public: + constexpr ALWAYS_INLINE KInterruptTask() : next_task(nullptr) { /* ... */ } + + ALWAYS_INLINE KInterruptTask *GetNextTask() const { + return this->next_task; + } + + ALWAYS_INLINE void SetNextTask(KInterruptTask *t) { + this->next_task = t; + } + + virtual void DoTask() = 0; + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task_manager.hpp new file mode 100644 index 000000000..cc3111b7a --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_task_manager.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::kern { + + class KThread; + + class KInterruptTaskManager { + private: + class TaskQueue { + private: + KInterruptTask *head; + KInterruptTask *tail; + public: + constexpr TaskQueue() : head(nullptr), tail(nullptr) { /* ... */ } + + ALWAYS_INLINE KInterruptTask *GetHead() { return this->head; } + ALWAYS_INLINE bool IsEmpty() const { return this->head == nullptr; } + ALWAYS_INLINE void Clear() { this->head = nullptr; this->tail = nullptr; } + + void Enqueue(KInterruptTask *task); + void Dequeue(); + }; + private: + TaskQueue task_queue; + KThread *thread; + public: + /* TODO: Actually implement KInterruptTaskManager. This is a placeholder. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 020757b5d..cabb55c4c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -402,6 +402,18 @@ namespace ams::kern { return GetVirtualMemoryBlockTree().FindFirstBlockByTypeAttr(KMemoryRegionType_KernelMiscMainStack, static_cast(core_id))->GetEndAddress(); } + static NOINLINE KVirtualAddress GetIdleStackTopAddress(s32 core_id) { + return GetVirtualMemoryBlockTree().FindFirstBlockByTypeAttr(KMemoryRegionType_KernelMiscIdleStack, static_cast(core_id))->GetEndAddress(); + } + + static NOINLINE KVirtualAddress GetExceptionStackBottomAddress(s32 core_id) { + return GetVirtualMemoryBlockTree().FindFirstBlockByTypeAttr(KMemoryRegionType_KernelMiscExceptionStack, static_cast(core_id))->GetAddress(); + } + + static NOINLINE KVirtualAddress GetCoreLocalRegionAddress() { + return GetVirtualMemoryBlockTree().FindFirstBlockByTypeAttr(KMemoryRegionType_CoreLocal)->GetAddress(); + } + static void InitializeLinearMemoryBlockTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start); }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp new file mode 100644 index 000000000..7476d1aea --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::kern { + + class KScheduler { + NON_COPYABLE(KScheduler); + NON_MOVEABLE(KScheduler); + public: + struct SchedulingState { + std::atomic needs_scheduling; + bool interrupt_task_thread_runnable; + bool should_count_idle; + u64 idle_count; + KThread *highest_priority_thread; + void *idle_thread_stack; + }; + private: + SchedulingState state; + bool is_active; + s32 core_id; + KThread *prev_thread; + u64 last_context_switch_time; + KThread *idle_thread; + public: + KScheduler(); + /* TODO: Actually implement KScheduler. This is a placeholder. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_spin_lock.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_spin_lock.hpp new file mode 100644 index 000000000..55706313c --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_spin_lock.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "kern_panic.hpp" + +#if defined(ATMOSPHERE_ARCH_ARM64) + + #include + namespace ams::kern { + using ams::kern::arm64::KAlignedSpinLock; + using ams::kern::arm64::KNotAlignedSpinLock; + using ams::kern::arm64::KSpinLock; + } + +#else + + #error "Unknown architecture for KInterruptManager" + +#endif + + +namespace ams::kern { + + class KScopedSpinLock { + private: + KSpinLock *lock_ptr; + public: + explicit ALWAYS_INLINE KScopedSpinLock(KSpinLock *l) : lock_ptr(l) { + this->lock_ptr->Lock(); + } + ALWAYS_INLINE ~KScopedSpinLock() { + this->lock_ptr->Unlock(); + } + }; + + class KScopedAlignedSpinLock { + private: + KAlignedSpinLock *lock_ptr; + public: + explicit ALWAYS_INLINE KScopedAlignedSpinLock(KAlignedSpinLock *l) : lock_ptr(l) { + this->lock_ptr->Lock(); + } + ALWAYS_INLINE ~KScopedAlignedSpinLock() { + this->lock_ptr->Unlock(); + } + }; + + class KScopedNotAlignedSpinLock { + private: + KNotAlignedSpinLock *lock_ptr; + public: + explicit ALWAYS_INLINE KScopedNotAlignedSpinLock(KNotAlignedSpinLock *l) : lock_ptr(l) { + this->lock_ptr->Lock(); + } + ALWAYS_INLINE ~KScopedNotAlignedSpinLock() { + this->lock_ptr->Unlock(); + } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp new file mode 100644 index 000000000..8959a381e --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +namespace ams::kern { + + + class KThread { + /* TODO: This should be a KAutoObject, and this is a placeholder definition. */ + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_timer_task.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_timer_task.hpp new file mode 100644 index 000000000..d57899ce8 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_timer_task.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::kern { + + class KTimerTask : public util::IntrusiveRedBlackTreeBaseNode { + private: + s64 time; + public: + static constexpr ALWAYS_INLINE int Compare(const KTimerTask &lhs, const KTimerTask &rhs) { + if (lhs.GetTime() < rhs.GetTime()) { + return -1; + } else { + return 1; + } + } + public: + constexpr ALWAYS_INLINE KTimerTask() : time(0) { /* ... */ } + + constexpr ALWAYS_INLINE void SetTime(s64 t) { + this->time = t; + } + + constexpr ALWAYS_INLINE s64 GetTime() const { + return this->time; + } + + virtual void OnTimer() = 0; + + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp new file mode 100644 index 000000000..4eb1e97d5 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::kern { + + class Kernel { + public: + enum class State : u8 { + Invalid = 0, + Initializing = 1, + Initialized = 2, + }; + private: + static inline State s_state = State::Invalid; + public: + static void Initialize(s32 core_id); + + static ALWAYS_INLINE State GetState() { return s_state; } + static ALWAYS_INLINE void SetState(State state) { s_state = state; } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_hardware_timer.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_hardware_timer.hpp new file mode 100644 index 000000000..db8d6e4f4 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_select_hardware_timer.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "kern_panic.hpp" + +#if defined(ATMOSPHERE_ARCH_ARM64) + + #include + namespace ams::kern { + using ams::kern::arm64::KHardwareTimer; + } + +#else + + #error "Unknown architecture for KHardwareTimer" + +#endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp new file mode 100644 index 000000000..e9bbd2ff6 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "kern_panic.hpp" + +#if defined(ATMOSPHERE_ARCH_ARM64) + + #include + namespace ams::kern { + using ams::kern::arm64::KInterruptController; + } + +#else + + #error "Unknown architecture for KInterruptController" + +#endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_manager.hpp similarity index 58% rename from libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp rename to libraries/libmesosphere/include/mesosphere/kern_select_interrupt_manager.hpp index 312450b7c..cab08dd0d 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_manager.hpp @@ -17,26 +17,42 @@ #include #include "kern_panic.hpp" -namespace ams::kern { +#if defined(ATMOSPHERE_ARCH_ARM64) - /* TODO: Actually select between architecture-specific interrupt code. */ + #include + namespace ams::kern { + using ams::kern::arm64::KInterruptManager; + } + +#else + + #error "Unknown architecture for KInterruptManager" + +#endif + + +namespace ams::kern { /* Enable or disable interrupts for the lifetime of an object. */ class KScopedInterruptDisable { NON_COPYABLE(KScopedInterruptDisable); NON_MOVEABLE(KScopedInterruptDisable); + private: + u32 prev_intr_state; public: - KScopedInterruptDisable(); - ~KScopedInterruptDisable(); + ALWAYS_INLINE KScopedInterruptDisable() : prev_intr_state(KInterruptManager::DisableInterrupts()) { /* ... */ } + ~KScopedInterruptDisable() { KInterruptManager::RestoreInterrupts(prev_intr_state); } }; class KScopedInterruptEnable { NON_COPYABLE(KScopedInterruptEnable); NON_MOVEABLE(KScopedInterruptEnable); + private: + u32 prev_intr_state; public: - KScopedInterruptEnable(); - ~KScopedInterruptEnable(); + ALWAYS_INLINE KScopedInterruptEnable() : prev_intr_state(KInterruptManager::EnableInterrupts()) { /* ... */ } + ~KScopedInterruptEnable() { KInterruptManager::RestoreInterrupts(prev_intr_state); } }; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp new file mode 100644 index 000000000..6dbf242b3 --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern::arm64 { + + void KHardwareTimer::DoTask() { + /* TODO: Actually implement this. */ + } + +} diff --git a/mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp similarity index 54% rename from mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp rename to libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp index 00ef6f718..8daa3d49d 100644 --- a/mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp @@ -17,20 +17,24 @@ namespace ams::kern { - inline KScopedInterruptDisable::KScopedInterruptDisable() { - /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + void KInterruptTaskManager::TaskQueue::Enqueue(KInterruptTask *task) { + /* Insert the task into the queue. */ + if (this->tail != nullptr) { + this->tail->SetNextTask(task); + } else { + this->head = task; + } + + this->tail = task; } - inline KScopedInterruptDisable::~KScopedInterruptDisable() { - /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ - } - - inline KScopedInterruptEnable::KScopedInterruptEnable() { - /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ - } - - inline KScopedInterruptEnable::~KScopedInterruptEnable() { - /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + void KInterruptTaskManager::TaskQueue::Dequeue() { + if (this->head == this->tail) { + this->head = nullptr; + this->tail = nullptr; + } else { + this->head = this->head->GetNextTask(); + } } } diff --git a/libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp similarity index 60% rename from libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp rename to libraries/libmesosphere/source/kern_k_scheduler.cpp index b342684db..1ec51bd8a 100644 --- a/libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -17,20 +17,15 @@ namespace ams::kern { - WEAK_SYMBOL KScopedInterruptDisable::KScopedInterruptDisable() { - /* TODO: Disable interrupts. */ - } - - WEAK_SYMBOL KScopedInterruptDisable::~KScopedInterruptDisable() { - /* TODO: un-disable interrupts. */ - } - - WEAK_SYMBOL KScopedInterruptEnable::KScopedInterruptEnable() { - /* TODO: Enable interrupts. */ - } - - WEAK_SYMBOL KScopedInterruptEnable::~KScopedInterruptEnable() { - /* TODO: un-enable interrupts. */ + KScheduler::KScheduler() + : is_active(false), core_id(0), prev_thread(nullptr), last_context_switch_time(0), idle_thread(nullptr) + { + this->state.needs_scheduling = true; + this->state.interrupt_task_thread_runnable = false; + this->state.should_count_idle = false; + this->state.idle_count = 0; + this->state.idle_thread_stack = nullptr; + this->state.highest_priority_thread = nullptr; } } diff --git a/libraries/libmesosphere/source/kern_kernel.cpp b/libraries/libmesosphere/source/kern_kernel.cpp new file mode 100644 index 000000000..e00cdb563 --- /dev/null +++ b/libraries/libmesosphere/source/kern_kernel.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + NOINLINE void Kernel::Initialize(s32 core_id) { + /* Construct the core local region object in place. */ + KCoreLocalContext *clc = GetPointer(KMemoryLayout::GetCoreLocalRegionAddress()); + new (clc) KCoreLocalContext; + + /* Set the core local region address into the global register. */ + cpu::SetCoreLocalRegionAddress(reinterpret_cast(clc)); + + /* Initialize current context. */ + clc->current.current_thread = nullptr; + clc->current.current_process = nullptr; + clc->current.scheduler = std::addressof(clc->scheduler); + clc->current.interrupt_task_manager = std::addressof(clc->interrupt_task_manager); + clc->current.core_id = core_id; + clc->current.exception_stack_bottom = GetVoidPointer(KMemoryLayout::GetExceptionStackBottomAddress(core_id)); + + /* Clear debugging counters. */ + clc->num_sw_interrupts = 0; + clc->num_hw_interrupts = 0; + clc->num_svc = 0; + clc->num_process_switches = 0; + clc->num_thread_switches = 0; + clc->num_fpu_switches = 0; + + for (size_t i = 0; i < util::size(clc->perf_counters); i++) { + clc->perf_counters[i] = 0; + } + } + +} diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index da4c7de80..0906fe3fc 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -18,7 +18,14 @@ namespace ams::kern { NORETURN void HorizonKernelMain(s32 core_id) { + /* Setup the Core Local Region, and note that we're initializing. */ + Kernel::Initialize(core_id); + Kernel::SetState(Kernel::State::Initializing); + + /* Ensure that all cores get to this point before proceeding. */ cpu::SynchronizeAllCores(); + + /* TODO: Implement more of Main() */ while (true) { /* ... */ } }