blob: d6e43518228f1c84c2f8eb096d1d2f10666934a1 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/arch/cache.h>
#include <lib/arch/x86/boot-cpuid.h>
#include <lib/arch/x86/bug.h>
#include <lib/boot-options/boot-options.h>
#include <lib/code-patching/code-patches.h>
#include <zircon/assert.h>
#include <cstdint>
#include <cstdio>
#include <arch/code-patches/case-id.h>
#include <arch/x86/cstring/selection.h>
#include <arch/x86/retpoline/selection.h>
#include <arch/x86/user-copy/selection.h>
#include <hwreg/x86msr.h>
namespace {
// TODO(68585): While .code-patches is allocated and accessed from directly
// within the kernel, we expect its recorded addresses to be the final,
// link-time ones.
ktl::span<ktl::byte> GetInstructions(uint64_t range_start, size_t range_size) {
return {reinterpret_cast<ktl::byte*>(range_start), range_size};
}
void PrintCaseInfo(const code_patching::Directive& patch, const char* fmt, ...) {
printf("code-patching: ");
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
printf(": [%#lx, %#lx)\n", patch.range_start, patch.range_start + patch.range_size);
}
} // namespace
// Declared in <lib/code-patching/code-patches.h>.
void ArchPatchCode(ktl::span<const code_patching::Directive> patches) {
arch::BootCpuidIo cpuid;
hwreg::X86MsrIo msr;
// Will effect instruction-data cache consistency on destruction.
arch::CacheConsistencyContext sync_ctx;
for (const code_patching::Directive& patch : patches) {
ktl::span<ktl::byte> insns = GetInstructions(patch.range_start, patch.range_size);
if (insns.empty()) {
ZX_PANIC("code-patching: unrecognized address range for patch case ID %u: [%#lx, %#lx)",
patch.id, patch.range_start, patch.range_start + patch.range_size);
}
switch (patch.id) {
case CASE_ID_SWAPGS_MITIGATION: {
// `nop` out the mitigation if the bug is not present, if we could not
// mitigate it even if it was, or if we generally want mitigations off.
const bool present = arch::HasX86SwapgsBug(cpuid);
if (!present || gBootOptions->x86_disable_spec_mitigations) {
code_patching::NopFill(insns);
ktl::string_view qualifier = !present ? "bug not present" : "all mitigations disabled";
PrintCaseInfo(patch, "swapgs bug mitigation disabled (%V)", qualifier);
break;
}
PrintCaseInfo(patch, "swapgs bug mitigation enabled");
continue; // No patching, so skip past sync'ing.
}
case CASE_ID_MDS_TAA_MITIGATION: {
// `nop` out the mitigation if the bug is not present, if we could not
// mitigate it even if it was, or if we generally want mitigations off.
const bool present = arch::HasX86MdsTaaBugs(cpuid, msr);
const bool can_mitigate = arch::CanMitigateX86MdsTaaBugs(cpuid);
if (!present || !can_mitigate || gBootOptions->x86_disable_spec_mitigations) {
code_patching::NopFill(insns);
ktl::string_view qualifier = !present ? "bug not present"
: !can_mitigate ? "unable to mitigate"
: "all mitigations disabled";
PrintCaseInfo(patch, "MDS/TAA bug mitigation disabled (%V)", qualifier);
break;
}
PrintCaseInfo(patch, "MDS/TAA bug mitigation enabled");
continue; // No patching, so skip past sync'ing.
}
case CASE_ID__X86_COPY_TO_OR_FROM_USER: {
ktl::string_view name = SelectX86UserCopyAlternative(cpuid);
auto alternative = GetPatchAlternative(name);
code_patching::Patch(insns, alternative);
PrintCaseInfo(patch, "using user-copy alternative \"%V\"", name);
break;
}
case CASE_ID___X86_INDIRECT_THUNK_R11: {
ktl::string_view name = SelectX86RetpolineAlternative(cpuid, msr, *gBootOptions);
auto alternative = GetPatchAlternative(name);
code_patching::Patch(insns, alternative);
PrintCaseInfo(patch, "using retpoline alternative \"%V\"", name);
break;
}
case CASE_ID___UNSANITIZED_MEMCPY: {
ktl::string_view name = SelectX86MemcpyAlternative(cpuid);
auto alternative = GetPatchAlternative(name);
code_patching::Patch(insns, alternative);
PrintCaseInfo(patch, "using memcpy alternative \"%V\"", name);
break;
}
case CASE_ID___UNSANITIZED_MEMSET: {
ktl::string_view name = SelectX86MemsetAlternative(cpuid);
auto alternative = GetPatchAlternative(name);
code_patching::Patch(insns, alternative);
PrintCaseInfo(patch, "using memset alternative \"%V\"", name);
break;
}
default:
ZX_PANIC("code-patching: unrecognized patch case ID: %u: [%#lx, %#lx)\n", patch.id,
patch.range_start, patch.range_start + patch.range_size);
}
sync_ctx.SyncRange(patch.range_start, patch.range_size);
}
}