kern: implement dynamic slab init + ini relocation

This commit is contained in:
Michael Scire 2020-02-07 04:58:35 -08:00
parent d9e6771e63
commit cb6af379d8
20 changed files with 851 additions and 22 deletions

View file

@ -17,6 +17,12 @@
namespace ams::kern::arm64 {
/* Instantiate static members in specific translation unit. */
KSpinLock KInterruptManager::s_lock;
std::array<KInterruptManager::KGlobalInterruptEntry, KInterruptController::NumGlobalInterrupts> KInterruptManager::s_global_interrupts;
KInterruptController::GlobalState KInterruptManager::s_global_state;
bool KInterruptManager::s_global_state_saved;
void KInterruptManager::Initialize(s32 core_id) {
this->interrupt_controller.Initialize(core_id);
}

View file

@ -233,6 +233,10 @@ namespace ams::kern {
}
}
u32 KSystemControl::GetInitialProcessBinaryPool() {
return KMemoryManager::Pool_Application;
}
/* Randomness. */
void KSystemControl::GenerateRandomBytes(void *dst, size_t size) {
MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
namespace {
KVirtualAddress GetInitialProcessBinaryAddress() {
return KMemoryLayout::GetPageTableHeapRegion().GetEndAddress() - InitialProcessBinarySizeMax;
}
void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) {
if (header->magic != InitialProcessBinaryMagic) {
*header = *GetPointer<InitialProcessBinaryHeader>(GetInitialProcessBinaryAddress());
}
MESOSPHERE_ABORT_UNLESS(header->magic == InitialProcessBinaryMagic);
MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess);
}
KVirtualAddress g_initial_process_binary_address;
InitialProcessBinaryHeader g_initial_process_binary_header;
u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
}
u64 GetInitialProcessIdMin() {
return g_initial_process_id_min;
}
u64 GetInitialProcessIdMax() {
return g_initial_process_id_max;
}
void CopyInitialProcessBinaryToKernelMemory() {
LoadInitialProcessBinaryHeader(&g_initial_process_binary_header);
if (g_initial_process_binary_header.num_processes > 0) {
/* Reserve pages for the initial process binary from the system resource limit. */
auto &mm = Kernel::GetMemoryManager();
const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize);
const size_t num_pages = total_size / PageSize;
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size));
/* Allocate memory for the image. */
const KMemoryManager::Pool pool = static_cast<KMemoryManager::Pool>(KSystemControl::GetInitialProcessBinaryPool());
const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront);
KVirtualAddress allocated_memory = mm.AllocateContinuous(num_pages, 1, allocate_option);
MESOSPHERE_ABORT_UNLESS(allocated_memory != Null<KVirtualAddress>);
mm.Open(allocated_memory, num_pages);
/* Relocate the image. */
std::memmove(GetVoidPointer(allocated_memory), GetVoidPointer(GetInitialProcessBinaryAddress()), g_initial_process_binary_header.size);
std::memset(GetVoidPointer(GetInitialProcessBinaryAddress()), 0, g_initial_process_binary_header.size);
g_initial_process_binary_address = allocated_memory;
}
}
}

View file

@ -0,0 +1,101 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
void KPageGroup::Initialize(KBlockInfoManager *m) {
this->manager = m;
}
void KPageGroup::Finalize() {
auto it = this->block_list.begin();
while (it != this->block_list.end()) {
KBlockInfo *info = std::addressof(*it);
it = this->block_list.erase(it);
this->manager->Free(info);
}
}
size_t KPageGroup::GetNumPages() const {
size_t num_pages = 0;
for (const auto &it : *this) {
num_pages += it.GetNumPages();
}
return num_pages;
}
Result KPageGroup::AddBlock(KVirtualAddress addr, size_t num_pages) {
/* Succeed immediately if we're adding no pages. */
R_UNLESS(num_pages != 0, ResultSuccess());
/* Check for overflow. */
MESOSPHERE_ASSERT(addr < addr + num_pages * PageSize);
/* Try to just append to the last block. */
if (!this->block_list.empty()) {
auto it = --(this->block_list.end());
R_UNLESS(!it->TryConcatenate(addr, num_pages), ResultSuccess());
}
/* Allocate a new block. */
KBlockInfo *new_block = this->manager->Allocate();
R_UNLESS(new_block != nullptr, svc::ResultOutOfResource());
/* Initialize the block. */
new_block->Initialize(addr, num_pages);
this->block_list.push_back(*new_block);
return ResultSuccess();
}
void KPageGroup::Open() const {
auto &mm = Kernel::GetMemoryManager();
for (const auto &it : *this) {
mm.Open(it.GetAddress(), it.GetNumPages());
}
}
void KPageGroup::Close() const {
auto &mm = Kernel::GetMemoryManager();
for (const auto &it : *this) {
mm.Close(it.GetAddress(), it.GetNumPages());
}
}
bool KPageGroup::IsEquivalentTo(const KPageGroup &rhs) const {
auto lit = this->block_list.cbegin();
auto rit = rhs.block_list.cbegin();
auto lend = this->block_list.cend();
auto rend = rhs.block_list.cend();
while (lit != lend && rit != rend) {
if (*lit != *rit) {
return false;
}
++lit;
++rit;
}
return lit == lend && rit == rend;
}
}

View file

@ -55,8 +55,8 @@ namespace ams::kern {
const size_t needed_size = this->blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(this->num_blocks); i++) {
if (const KVirtualAddress addr = this->blocks[index].PopBlock(); addr != Null<KVirtualAddress>) {
if (const size_t allocated_size = this->blocks[index].GetSize(); allocated_size > needed_size) {
if (const KVirtualAddress addr = this->blocks[i].PopBlock(); addr != Null<KVirtualAddress>) {
if (const size_t allocated_size = this->blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
return addr;

View file

@ -18,11 +18,15 @@
namespace ams::kern {
/* Declare kernel data members in kernel TU. */
Kernel::State Kernel::s_state = Kernel::State::Invalid;
KThread Kernel::s_main_threads[cpu::NumCores];
KThread Kernel::s_idle_threads[cpu::NumCores];
KResourceLimit Kernel::s_system_resource_limit;
KMemoryManager Kernel::s_memory_manager;
Kernel::State Kernel::s_state = Kernel::State::Invalid;
KThread Kernel::s_main_threads[cpu::NumCores];
KThread Kernel::s_idle_threads[cpu::NumCores];
KResourceLimit Kernel::s_system_resource_limit;
KMemoryManager Kernel::s_memory_manager;
KPageTableManager Kernel::s_page_table_manager;
KMemoryBlockSlabManager Kernel::s_app_memory_block_manager;
KMemoryBlockSlabManager Kernel::s_sys_memory_block_manager;
KBlockInfoManager Kernel::s_block_info_manager;
void Kernel::InitializeCoreLocalRegion(s32 core_id) {
/* Construct the core local region object in place. */
@ -68,10 +72,32 @@ namespace ams::kern {
SetCurrentThread(main_thread);
SetCurrentProcess(nullptr);
/* TODO: Initialize the interrupt manager. */
/* Initialize the interrupt manager, hardware timer, and scheduler */
GetInterruptManager().Initialize(core_id);
GetHardwareTimer().Initialize(core_id);
GetScheduler().Initialize(idle_thread);
}
void Kernel::InitializeResourceManagers(KVirtualAddress address, size_t size) {
/* Ensure that the buffer is suitable for our use. */
const size_t app_size = ApplicationMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
const size_t sys_size = SystemMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
const size_t info_size = BlockInfoSlabHeapSize * sizeof(KBlockInfo);
const size_t fixed_size = util::AlignUp(app_size + sys_size + info_size, PageSize);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(fixed_size < size);
size_t pt_size = size - fixed_size;
const size_t rc_size = util::AlignUp(KPageTableManager::CalculateReferenceCountSize(pt_size), PageSize);
MESOSPHERE_ABORT_UNLESS(rc_size < pt_size);
pt_size -= rc_size;
/* Initialize the slabheaps. */
s_app_memory_block_manager.Initialize(address + pt_size, app_size);
s_sys_memory_block_manager.Initialize(address + pt_size + app_size, sys_size);
s_block_info_manager.Initialize(address + pt_size + app_size + sys_size, info_size);
s_page_table_manager.Initialize(address, pt_size, GetPointer<KPageTableManager::RefCount>(address + pt_size + fixed_size));
}
}

View file

@ -45,10 +45,17 @@ namespace ams::kern {
init::InitializeKPageBufferSlabHeap();
}
/* Note: this is not actually done here, it's done later in main after more stuff is setup. */
/* However, for testing (and to manifest this code in the produced binary, this is here for now. */
/* TODO: Do this better. */
/* Copy the Initial Process Binary to safe memory. */
CopyInitialProcessBinaryToKernelMemory();
/* Initialize the KObject Slab Heaps. */
init::InitializeSlabHeaps();
/* Initialize the Dynamic Slab Heaps. */
{
const auto &pt_heap_region = KMemoryLayout::GetPageTableHeapRegion();
Kernel::InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize());
}
}
/* TODO: Implement more of Main() */