diff --git a/stratosphere/loader/source/ldr_capabilities.cpp b/stratosphere/loader/source/ldr_capabilities.cpp index 2766fe26c..6b0600a8c 100644 --- a/stratosphere/loader/source/ldr_capabilities.cpp +++ b/stratosphere/loader/source/ldr_capabilities.cpp @@ -47,6 +47,9 @@ namespace ams::ldr::caps { return static_cast(__builtin_ctz(~cap.value)); } + constexpr inline util::BitPack32 EmptyCapability = {~u32{}}; + static_assert(GetCapabilityId(EmptyCapability) == CapabilityId::Empty); + #define CAPABILITY_CLASS_NAME(id) Capability##id #define DEFINE_CAPABILITY_CLASS(id, member_functions) \ @@ -407,4 +410,28 @@ namespace ams::ldr::caps { } } + void ProcessCapabilities(void *kac, size_t kac_size) { + util::BitPack32 *caps = reinterpret_cast(kac); + const size_t num_caps = kac_size / sizeof(*caps); + + for (size_t i = 0; i < num_caps; i++) { + const auto cur_cap = caps[i]; + switch (GetCapabilityId(cur_cap)) { + case CapabilityId::MapRegion: + { + /* MapRegion was added in 8.0.0+. */ + /* To prevent kernel error, we should reject the descriptor on lower firmwares. */ + /* NOTE: We also allow it on any firmware under mesosphere, as an extension. */ + const bool is_allowed = (hos::GetVersion() >= hos::Version_8_0_0 || svc::IsKernelMesosphere()); + if (!is_allowed) { + caps[i] = EmptyCapability; + } + } + break; + default: + break; + } + } + } + } diff --git a/stratosphere/loader/source/ldr_capabilities.hpp b/stratosphere/loader/source/ldr_capabilities.hpp index 07ae0e9c9..acaa9873f 100644 --- a/stratosphere/loader/source/ldr_capabilities.hpp +++ b/stratosphere/loader/source/ldr_capabilities.hpp @@ -23,4 +23,6 @@ namespace ams::ldr::caps { u16 GetProgramInfoFlags(const void *kac, size_t kac_size); void SetProgramInfoFlags(u16 flags, void *kac, size_t kac_size); + void ProcessCapabilities(void *kac, size_t kac_size); + } diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp index 75c7ac666..2073f6d20 100644 --- a/stratosphere/loader/source/ldr_meta.cpp +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -225,6 +225,11 @@ namespace ams::ldr { } } + /* Pre-process the capabilities. */ + /* This is used to e.g. avoid passing memory region descriptor to older kernels. */ + caps::ProcessCapabilities(meta->acid_kac, meta->acid->kac_size); + caps::ProcessCapabilities(meta->aci_kac, meta->aci->kac_size); + /* Set output. */ g_cached_program_id = loc.program_id; g_cached_override_status = status;