root/src/system/kernel/arch/x86/64/errata.cpp
/*
 * Copyright 2019-2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Augustin Cavalier <waddlesplash>
 */

#include <cpu.h>
#include <arch_cpu.h>


static bool
needs_zenbleed_patch(const uint32 family, const uint32 model, const uint64 microcode)
{
        if (family != 0x17)
                return false;

        // https://github.com/torvalds/linux/blob/522b1d6921/arch/x86/kernel/cpu/amd.c#L986
        if (model >= 0x30 && model <= 0x3f)
                return (microcode < 0x0830107a);
        if (model >= 0x60 && model <= 0x67)
                return (microcode < 0x0860010b);
        if (model >= 0x68 && model <= 0x6f)
                return (microcode < 0x08608105);
        if (model >= 0x70 && model <= 0x7f)
                return (microcode < 0x08701032);
        if (model >= 0xa0 && model <= 0xaf)
                return (microcode < 0x08a00008);

        return false;
}


static status_t
patch_errata_percpu_amd(int currentCPU, const cpu_ent* cpu)
{
        // There are no errata to patch on a hypervisor, the host will have
        // already taken care of everything for us.
        if (x86_check_feature(IA32_FEATURE_EXT_HYPERVISOR, FEATURE_EXT))
                return B_OK;

        const uint32 family = cpu->arch.family + cpu->arch.extended_family,
                model = (cpu->arch.extended_model << 4) | cpu->arch.model;
        const uint64 microcode = x86_read_msr(IA32_MSR_UCODE_REV);

        // Family 10h and 12h, Erratum 721:
        //
        // "Under a highly specific and detailed set of internal timing conditions,
        // the processor may incorrectly update the stack pointer after a long
        // series of push and/or near-call instructions, or a long series of pop
        // and/or near-return instructions.
        //
        // https://www.amd.com/system/files/TechDocs/41322_10h_Rev_Gd.pdf
        // https://www.amd.com/system/files/TechDocs/44739_12h_Rev_Gd.pdf
        switch (family) {
        case 0x10:
        case 0x12:
                x86_write_msr(0xc0011029, x86_read_msr(0xc0011029) | 1);
                break;
        }

        // Family 16h ("Jaguar"), Erratum 793:
        //
        // "Under a highly specific and detailed set of internal timing conditions,
        // a locked instruction may trigger a timing sequence whereby the write to
        // a write combined memory type is not flushed, causing the locked instruction
        // to stall indefinitely."
        //
        // https://www.amd.com/system/files/TechDocs/53072_Rev_Guide_16h_Models_30h-3Fh.pdf
        if (family == 0x16 && model <= 0xf) {
                x86_write_msr(0xc0011020, x86_read_msr(0xc0011020) | ((uint64)1 << 15));
        }

        // Family 17h ("Zen") Model 1h
        // https://www.amd.com/system/files/TechDocs/55449_Fam_17h_M_00h-0Fh_Rev_Guide.pdf
        if (family == 0x17 && model == 0x1) {
                // Erratum 1021:
                //
                // "Under a highly specific and detailed set of internal timing
                // conditions, a load operation may incorrectly receive stale data
                // from an older store operation."
                x86_write_msr(0xc0011029, x86_read_msr(0xc0011029) | (1 << 13));

                // Erratum 1033:
                //
                // "Under a highly specific and detailed set of internal timing
                // conditions, a Lock operation may cause the system to hang."
                x86_write_msr(0xc0011020, x86_read_msr(0xc0011020) | (1 << 4));

                // Erratum 1049:
                //
                // "Under a highly specific and detailed set of internal timing
                // conditions, an FCMOV instruction may yield incorrect data if the
                // following sequence of events occurs:
                //  * An FCOMI instruction
                //  * A non-FP instruction that modifies RFLAGS
                //  * An FCMOV instruction."
                x86_write_msr(0xc0011028, x86_read_msr(0xc0011028) | (1 << 4));

                // Erratum 1095:
                //
                // Under a highly detailed and specific set of internal timing
                // conditions, a lock operation may not fence a younger load operation
                // correctly when the following conditions are met:
                //  * SMT (Simultaneous Multithreading) is enabled, and
                //  * a lock operation on memory location A, followed by a load
                //    operation on memory location B are executing on one thread while
                //  * a lock operation on memory location B, followed by a load operation
                //    on memory location A are executing on the second thread on the
                //    same core.
                // This may result in the load operations on both threads incorrectly
                // receiving pre-lock data."
                x86_write_msr(0xc0011020, x86_read_msr(0xc0011020) | ((uint64)1 << 57));
        }

        // Family 17h ("Zen")
        // Cross-Process Information Leak (aka. "Zenbleed")
        // https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7008.html
        if (needs_zenbleed_patch(family, model, microcode)) {
                x86_write_msr(MSR_F10H_DE_CFG, x86_read_msr(MSR_F10H_DE_CFG) | (1 << 9));
        }

        return B_OK;
}


status_t
__x86_patch_errata_percpu(int currentCPU)
{
        const cpu_ent* cpu = get_cpu_struct();
        if (cpu->arch.vendor == VENDOR_AMD
                || cpu->arch.vendor == VENDOR_HYGON) {
                return patch_errata_percpu_amd(currentCPU, cpu);
        }
        return B_OK;
}