From ef1763334bd3159190bfc200f2581260cf23b7af Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 13 Jul 2020 19:16:05 -0700 Subject: [PATCH] kern: patch svc tables via asm, instead of relying on compiler to be nice --- .../source/arch/arm64/svc/kern_svc_tables.cpp | 19 ++++++------- .../arch/arm64/svc/kern_svc_tables_asm.s | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 mesosphere/kernel/source/arch/arm64/svc/kern_svc_tables_asm.s diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp index e2a1684fa..9e7510f1f 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp @@ -92,6 +92,8 @@ namespace ams::kern::svc { constinit const std::array SvcTable64From32 = SvcTable64From32Impl; + void PatchSvcTableEntry(const SvcTableEntry *table, u32 id, SvcTableEntry entry); + namespace { /* NOTE: Although the SVC tables are constants, our global constructor will run before .rodata is protected R--. */ @@ -101,34 +103,33 @@ namespace ams::kern::svc { private: static SvcTablePatcher s_instance; private: - ALWAYS_INLINE volatile SvcTableEntry *GetEditableTable(const SvcTable *table) { + ALWAYS_INLINE const SvcTableEntry *GetTableData(const SvcTable *table) { if (table != nullptr) { - return const_cast(table->data()); + return table->data(); } else { return nullptr; } } - /* TODO: This should perhaps be implemented in assembly. */ - NOINLINE void PatchTables(volatile SvcTableEntry *table_64, volatile SvcTableEntry *table_64_from_32) { + NOINLINE void PatchTables(const SvcTableEntry *table_64, const SvcTableEntry *table_64_from_32) { /* Get the target firmware. */ const auto target_fw = kern::GetTargetFirmware(); /* 10.0.0 broke the ABI for QueryIoMapping. */ if (target_fw < TargetFirmware_10_0_0) { - if (table_64) { table_64[ svc::SvcId_QueryIoMapping] = LegacyQueryIoMapping::Call64; } - if (table_64_from_32) { table_64_from_32[svc::SvcId_QueryIoMapping] = LegacyQueryIoMapping::Call64From32; } + if (table_64) { ::ams::kern::svc::PatchSvcTableEntry(table_64, svc::SvcId_QueryIoMapping, LegacyQueryIoMapping::Call64); } + if (table_64_from_32) { ::ams::kern::svc::PatchSvcTableEntry(table_64_from_32, svc::SvcId_QueryIoMapping, LegacyQueryIoMapping::Call64From32); } } /* 3.0.0 broke the ABI for ContinueDebugEvent. */ if (target_fw < TargetFirmware_3_0_0) { - if (table_64) { table_64[ svc::SvcId_ContinueDebugEvent] = LegacyContinueDebugEvent::Call64; } - if (table_64_from_32) { table_64_from_32[svc::SvcId_ContinueDebugEvent] = LegacyContinueDebugEvent::Call64From32; } + if (table_64) { ::ams::kern::svc::PatchSvcTableEntry(table_64, svc::SvcId_ContinueDebugEvent, LegacyContinueDebugEvent::Call64); } + if (table_64_from_32) { ::ams::kern::svc::PatchSvcTableEntry(table_64_from_32, svc::SvcId_ContinueDebugEvent, LegacyContinueDebugEvent::Call64From32); } } } public: SvcTablePatcher(const SvcTable *table_64, const SvcTable *table_64_from_32) { - PatchTables(GetEditableTable(table_64), GetEditableTable(table_64_from_32)); + PatchTables(GetTableData(table_64), GetTableData(table_64_from_32)); } }; diff --git a/mesosphere/kernel/source/arch/arm64/svc/kern_svc_tables_asm.s b/mesosphere/kernel/source/arch/arm64/svc/kern_svc_tables_asm.s new file mode 100644 index 000000000..324bdd5a4 --- /dev/null +++ b/mesosphere/kernel/source/arch/arm64/svc/kern_svc_tables_asm.s @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +/* ams::kern::svc::PatchSvcTableEntry(void (* const*)(), unsigned int, void (*)()) */ +.section .text._ZN3ams4kern3svc18PatchSvcTableEntryEPKPFvvEjS3_, "ax", %progbits +.global _ZN3ams4kern3svc18PatchSvcTableEntryEPKPFvvEjS3_ +.type _ZN3ams4kern3svc18PatchSvcTableEntryEPKPFvvEjS3_, %function +_ZN3ams4kern3svc18PatchSvcTableEntryEPKPFvvEjS3_: + /* This function violates const correctness by design, to patch the svc tables. */ + /* The svc tables live in .rodata (.rel.ro), but must be patched by initial constructors */ + /* to support firmware-specific table entries. */ + mov w1, w1 + str x2, [x0, x1, lsl #3] + ret \ No newline at end of file