From bf5bbfbcef68fba53c4187480ded06fb1cd12e57 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 31 Dec 2019 00:45:28 -0800 Subject: [PATCH] kernel_ldr: clean up KSystemControl init API --- .../nintendo/switch/kern_k_system_control.hpp | 18 +++-- .../nintendo/switch/kern_k_system_control.cpp | 41 +++++----- .../nintendo/switch/kern_secure_monitor.cpp | 80 +++++++++++++------ .../nintendo/switch/kern_secure_monitor.hpp | 19 +++-- .../kernel_ldr/source/kern_init_loader.cpp | 6 +- 5 files changed, 102 insertions(+), 62 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp index 93454413d..dfb256011 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp @@ -20,15 +20,17 @@ namespace ams::kern { class KSystemControl { public: + class Init { + public: + /* Initialization. */ + static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address); + static bool ShouldIncreaseThreadResourceLimit(); - /* Initialization. */ - static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address); - static bool ShouldIncreaseResourceRegionSize(); - - /* Randomness. */ - static void GenerateRandomBytes(void *dst, size_t size); - static u64 GenerateRandomRange(u64 min, u64 max); - + /* Randomness. */ + static void GenerateRandomBytes(void *dst, size_t size); + static u64 GenerateRandomRange(u64 min, u64 max); + }; + public: /* Panic. */ static NORETURN void StopSystem(); }; diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp index ba9898ad6..6746223c2 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp @@ -25,33 +25,32 @@ namespace ams::kern { constexpr size_t SixGigabytes = 0x180000000ul; constexpr size_t EightGigabytes = 0x200000000ul; - size_t GetRealMemorySize() { + ALWAYS_INLINE size_t GetRealMemorySizeForInit() { /* TODO: Move this into a header for the MC in general. */ constexpr u32 MemoryControllerConfigurationRegister = 0x70019050; u32 config_value; - MESOSPHERE_ABORT_UNLESS(smc::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0)); + MESOSPHERE_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0)); return static_cast(config_value & 0x3FFF) << 20; } - inline u64 GetKernelConfiguration() { + ALWAYS_INLINE u64 GetKernelConfigurationForInit() { u64 value = 0; - smc::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration); + smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration); return value; } - inline u64 GenerateRandomU64() { + ALWAYS_INLINE u64 GenerateRandomU64ForInit() { u64 value; - smc::GenerateRandomBytes(&value, sizeof(value)); + smc::init::GenerateRandomBytes(&value, sizeof(value)); return value; } - inline smc::MemoryMode GetMemoryMode() { - return static_cast((GetKernelConfiguration() >> 10) & 0x3); + ALWAYS_INLINE smc::MemoryMode GetMemoryModeForInit() { + return static_cast((GetKernelConfigurationForInit() >> 10) & 0x3); } - size_t GetIntendedMemorySize() { - const smc::MemoryMode memory_mode = GetMemoryMode(); - switch (memory_mode) { + ALWAYS_INLINE size_t GetIntendedMemorySizeForInit() { + switch (GetMemoryModeForInit()) { case smc::MemoryMode_4GB: default: /* All invalid modes should go to 4GB. */ return FourGigabytes; @@ -65,9 +64,9 @@ namespace ams::kern { } /* Initialization. */ - KPhysicalAddress KSystemControl::GetKernelPhysicalBaseAddress(uintptr_t base_address) { - const size_t real_dram_size = GetRealMemorySize(); - const size_t intended_dram_size = GetIntendedMemorySize(); + KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(uintptr_t base_address) { + const size_t real_dram_size = GetRealMemorySizeForInit(); + const size_t intended_dram_size = GetIntendedMemorySizeForInit(); if (intended_dram_size * 2 < real_dram_size) { return base_address; } else { @@ -75,21 +74,21 @@ namespace ams::kern { } } - bool KSystemControl::ShouldIncreaseResourceRegionSize() { - return (GetKernelConfiguration() >> 3) & 1; + bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { + return (GetKernelConfigurationForInit() >> 3) & 1; } - /* Randomness. */ - void KSystemControl::GenerateRandomBytes(void *dst, size_t size) { + /* Randomness for Initialization. */ + void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) { MESOSPHERE_ABORT_UNLESS(size <= 0x38); - smc::GenerateRandomBytes(dst, size); + smc::init::GenerateRandomBytes(dst, size); } - u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { + u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) { const u64 range_size = ((max + 1) - min); const u64 effective_max = (std::numeric_limits::max() / range_size) * range_size; while (true) { - if (const u64 rnd = GenerateRandomU64(); rnd < effective_max) { + if (const u64 rnd = GenerateRandomU64ForInit(); rnd < effective_max) { return min + (rnd % range_size); } } diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp index 8669ff32f..d2c654bfd 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp @@ -69,27 +69,68 @@ namespace ams::kern::smc { args.x[7] = x7; } - } + void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) { + /* Load arguments into registers. */ + register u64 x0 asm("x0") = args.x[0]; + register u64 x1 asm("x1") = args.x[1]; + register u64 x2 asm("x2") = args.x[2]; + register u64 x3 asm("x3") = args.x[3]; + register u64 x4 asm("x4") = args.x[4]; + register u64 x5 asm("x5") = args.x[5]; + register u64 x6 asm("x6") = args.x[6]; + register u64 x7 asm("x7") = args.x[7]; - void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { - SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; - CallPrivilegedSecureMonitorFunction(args); - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); - for (size_t i = 0; i < num_qwords && i < 7; i++) { - out[i] = args.x[1 + i]; + /* Actually make the call. */ + __asm__ __volatile__("smc #1" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory" + ); + + /* Store arguments to output. */ + args.x[0] = x0; + args.x[1] = x1; + args.x[2] = x2; + args.x[3] = x3; + args.x[4] = x4; + args.x[5] = x5; + args.x[6] = x6; + args.x[7] = x7; } + } - void GenerateRandomBytes(void *dst, size_t size) { - /* Call SmcGenerateRandomBytes() */ - /* TODO: Lock this to ensure only one core calls at once. */ - SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size }; - MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0])); - CallPrivilegedSecureMonitorFunction(args); - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + /* SMC functionality needed for init. */ + namespace init { + + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; + CallPrivilegedSecureMonitorFunctionForInit(args); + MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + for (size_t i = 0; i < num_qwords && i < 7; i++) { + out[i] = args.x[1 + i]; + } + } + + void GenerateRandomBytes(void *dst, size_t size) { + /* Call SmcGenerateRandomBytes() */ + /* TODO: Lock this to ensure only one core calls at once. */ + SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size }; + MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0])); + CallPrivilegedSecureMonitorFunctionForInit(args); + MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + + /* Copy output. */ + std::memcpy(dst, &args.x[1], size); + } + + bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) { + SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value }; + CallPrivilegedSecureMonitorFunctionForInit(args); + *out = args.x[1]; + return static_cast(args.x[0]) == SmcResult::Success; + } - /* Copy output. */ - std::memcpy(dst, &args.x[1], size); } void NORETURN Panic(u32 color) { @@ -98,11 +139,4 @@ namespace ams::kern::smc { while (true) { /* ... */ } } - bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) { - SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value }; - CallPrivilegedSecureMonitorFunction(args); - *out = args.x[1]; - return static_cast(args.x[0]) == SmcResult::Success; - } - } \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp index 4f74dac16..e3c88b2fa 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp @@ -36,13 +36,13 @@ namespace ams::kern::smc { IsRecoveryBoot = 7, DeviceId = 8, BootReason = 9, - MemoryArrange = 10, + MemoryMode = 10, IsDebugMode = 11, KernelConfiguration = 12, IsChargerHiZModeEnabled = 13, - IsKiosk = 14, - NewHardwareType = 15, - NewKeyGeneration = 16, + IsQuest = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, Package2Hash = 17, /* Extension config items for exosphere. */ @@ -64,9 +64,14 @@ namespace ams::kern::smc { }; /* TODO: Rest of Secure Monitor API. */ - void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); - void GenerateRandomBytes(void *dst, size_t size); void NORETURN Panic(u32 color); - bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); + + namespace init { + + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); + void GenerateRandomBytes(void *dst, size_t size); + bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); + + } } \ No newline at end of file diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index 7b7d5c562..26afd7026 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -66,7 +66,7 @@ namespace ams::kern::init::loader { void RelocateKernelPhysically(uintptr_t &base_address, KernelLayout *&layout) { /* TODO: Proper secure monitor call. */ - KPhysicalAddress correct_base = KSystemControl::GetKernelPhysicalBaseAddress(base_address); + KPhysicalAddress correct_base = KSystemControl::Init::GetKernelPhysicalBaseAddress(base_address); if (correct_base != base_address) { const uintptr_t diff = GetInteger(correct_base) - base_address; const size_t size = layout->rw_end_offset; @@ -218,7 +218,7 @@ namespace ams::kern::init::loader { /* Repeatedly generate a random virtual address until we get one that's unmapped in the destination page table. */ while (true) { - const KVirtualAddress random_kaslr_slide = KSystemControl::GenerateRandomRange(KernelBaseRangeMin, KernelBaseRangeEnd); + const KVirtualAddress random_kaslr_slide = KSystemControl::Init::GenerateRandomRange(KernelBaseRangeMin, KernelBaseRangeEnd); const KVirtualAddress kernel_region_start = util::AlignDown(GetInteger(random_kaslr_slide), KernelBaseAlignment); const KVirtualAddress kernel_region_end = util::AlignUp(GetInteger(kernel_region_start) + kernel_offset + kernel_size, KernelBaseAlignment); const size_t kernel_region_size = GetInteger(kernel_region_end) - GetInteger(kernel_region_start); @@ -275,7 +275,7 @@ namespace ams::kern::init::loader { const uintptr_t init_array_end_offset = layout->init_array_end_offset; /* Decide if Kernel should have enlarged resource region. */ - const bool use_extra_resources = KSystemControl::ShouldIncreaseResourceRegionSize(); + const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); const size_t resource_region_size = KernelResourceRegionSize + (use_extra_resources ? ExtraKernelResourceSize : 0); /* Setup the INI1 header in memory for the kernel. */