# vim: set ts=4 sw=4 sts=4 et:
# MMIO Stale Data (Processor MMIO Stale Data Vulnerabilities) - BSD mitigation check
check_mmio_bsd() {
    # No BSD (FreeBSD, OpenBSD, NetBSD, DragonFlyBSD) has implemented an OS-level
    # MMIO Stale Data mitigation. All four stopped at MDS/TAA. Microcode update is
    # the only partial defense available, and without OS-level VERW invocation it
    # cannot close the vulnerability.
    local unk
    if ! is_cpu_affected "$cve"; then
        pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
    elif is_cpu_mmio_unknown; then
        if is_arch_cap_mmio_undetermined; then
            # We only landed in the "unknown" bucket because the IA32_ARCH_CAPABILITIES
            # MSR couldn't be read: the CPU might actually advertise MMIO immunity.
            unk="your CPU's MMIO Stale Data status could not be determined: the IA32_ARCH_CAPABILITIES MSR (0x10a) couldn't be read"
            pvulnstatus "$cve" UNK "$unk; load the cpuctl module and/or re-run as root to get a definitive answer"
        else
            unk="your CPU's MMIO Stale Data status is unknown (Intel never officially assessed this CPU, its servicing period has ended)"
            if [ "$opt_paranoid" = 1 ]; then
                pvulnstatus "$cve" VULN "$unk, and no BSD mitigation exists"
                explain "There is no known mitigation for this CPU model. Even with up-to-date microcode, BSD kernels do not invoke VERW for MMIO Stale Data clearing. Only a hardware replacement can fully address this."
            else
                pvulnstatus "$cve" UNK "$unk; no BSD mitigation exists in any case"
            fi
        fi
    else
        pvulnstatus "$cve" VULN "your CPU is affected and no BSD has implemented an MMIO Stale Data mitigation"
        explain "No BSD kernel currently implements an MMIO Stale Data mitigation (which would require invoking VERW at context switches and VM-entries). Updating CPU microcode alone does not mitigate this vulnerability without OS cooperation."
    fi
}

# MMIO Stale Data (Processor MMIO Stale Data Vulnerabilities) - Linux mitigation check
check_mmio_linux() {
    local status sys_interface_available msg kernel_mmio kernel_mmio_can_tell kernel_mmio_unknown_aware mmio_mitigated mmio_smt_mitigated mystatus mymsg unk
    status=UNK
    sys_interface_available=0
    msg=''
    if sys_interface_check "$VULN_SYSFS_BASE/mmio_stale_data" '^[^;]+'; then
        # Kernel source inventory for MMIO Stale Data, traced via git blame walkback
        # across /shared/linux, /shared/linux-stable, and /shared/linux-centos-redhat:
        #
        # --- sysfs messages ---
        # all versions:
        #   "Not affected"                                                      (cpu_show_common, generic)
        #
        # 8cb861e9e3c9 (v5.19, initial MMIO mitigation, Pawan Gupta 2022-05-19):
        #   enum mmio_mitigations: MMIO_MITIGATION_OFF, MMIO_MITIGATION_UCODE_NEEDED, MMIO_MITIGATION_VERW
        #   mmio_strings[]:
        #     "Vulnerable"                                                      (MMIO_MITIGATION_OFF)
        #     "Vulnerable: Clear CPU buffers attempted, no microcode"           (MMIO_MITIGATION_UCODE_NEEDED)
        #     "Mitigation: Clear CPU buffers"                                   (MMIO_MITIGATION_VERW)
        #
        # 8d50cdf8b834 (v5.19, sysfs reporting, Pawan Gupta 2022-05-19):
        #   mmio_stale_data_show_state() added with SMT suffix:
        #     "{mmio_strings[state]}; SMT vulnerable"                           (sched_smt_active() true)
        #     "{mmio_strings[state]}; SMT disabled"                             (sched_smt_active() false)
        #     "{mmio_strings[state]}; SMT Host state unknown"                   (boot_cpu_has(HYPERVISOR))
        #   No SMT suffix when MMIO_MITIGATION_OFF.
        #   Uses sysfs_emit() in mainline. CentOS 7 backport uses sprintf().
        #
        # 7df548840c49 (v6.0, "unknown" reporting, Pawan Gupta 2022-08-03):
        #   Added X86_BUG_MMIO_UNKNOWN handling:
        #     "Unknown: No mitigations"                                         (X86_BUG_MMIO_UNKNOWN set)
        #   Present in: v6.0 through v6.15, stable 5.10.y/5.15.y/6.1.y/6.6.y, rocky8, rocky9
        #
        # dd86a1d013e0 (v6.16, removed MMIO_UNKNOWN, Borislav Petkov 2025-04-14):
        #   Removed X86_BUG_MMIO_UNKNOWN -- "Unknown" message no longer produced.
        #   Replaced by general X86_BUG_OLD_MICROCODE mechanism.
        #
        # 4a5a04e61d7f (v6.16, restructured, David Kaplan 2025-04-18):
        #   Split into select/update/apply pattern. Same strings, same output.
        #
        # all messages start with "Not affected", "Vulnerable", "Mitigation", or "Unknown"
        #
        # --- stable backports ---
        # Stable branches 5.4.y through 6.15.y: identical mmio_strings[] array.
        # 5.4.y uses sprintf(); 5.10.y+ uses sysfs_emit().
        # v6.0.y through v6.15.y include "Unknown: No mitigations" branch.
        # v6.16.y+: restructured, no "Unknown" message.
        #
        # --- RHEL/CentOS ---
        # centos7: sprintf() instead of sysfs_emit(), otherwise identical strings.
        # rocky8: sysfs_emit(), includes X86_BUG_MMIO_UNKNOWN.
        # rocky9: sysfs_emit(), includes X86_BUG_MMIO_UNKNOWN.
        # rocky10: restructured, matches mainline v6.16+.
        # All RHEL branches use identical mmio_strings[] array.
        #
        # --- Kconfig symbols ---
        # No Kconfig symbol: v5.19 through v6.11 (mitigation always compiled in when CPU_SUP_INTEL)
        # 163f9fe6b625 (v6.12, Breno Leitao 2024-07-29): CONFIG_MITIGATION_MMIO_STALE_DATA (bool, default y, depends CPU_SUP_INTEL)
        # No other name variants exist (no renames). Single symbol throughout history.
        #
        # --- stable ---
        # Only linux-rolling-lts and linux-rolling-stable have the Kconfig symbol.
        # Stable branches 5.x through 6.11.y: no Kconfig (always compiled in).
        #
        # --- RHEL ---
        # rocky9, rocky10: CONFIG_MITIGATION_MMIO_STALE_DATA present.
        # rocky8, centos7: no Kconfig symbol.
        #
        # --- kernel functions (for $opt_map / System.map) ---
        # 8cb861e9e3c9 (v5.19): mmio_select_mitigation() [static __init]
        # 8cb861e9e3c9 (v5.19): mmio_stale_data_parse_cmdline() [static __init]
        # 8d50cdf8b834 (v5.19): mmio_stale_data_show_state() [static]
        # 8d50cdf8b834 (v5.19): cpu_show_mmio_stale_data() [global, non-static -- visible in System.map]
        # 4a5a04e61d7f (v6.16): + mmio_update_mitigation() [static __init]
        # 4a5a04e61d7f (v6.16): + mmio_apply_mitigation() [static __init]
        #
        # Best grep targets for $opt_map: mmio_select_mitigation, cpu_show_mmio_stale_data
        # Best grep targets for $g_kernel: mmio_stale_data (appears in sysfs strings and parameter name)
        #
        # --- stable ---
        # 5.4.y-6.15.y: mmio_select_mitigation, mmio_stale_data_parse_cmdline, mmio_stale_data_show_state
        # 6.16.y+: + mmio_update_mitigation, mmio_apply_mitigation
        #
        # --- RHEL ---
        # rocky8/rocky9: mmio_select_mitigation, mmio_stale_data_parse_cmdline, mmio_stale_data_show_state
        # rocky10: + mmio_update_mitigation, mmio_apply_mitigation
        #
        # --- CPU affection logic (for is_cpu_affected) ---
        # 51802186158c (v5.19, initial model list, Pawan Gupta 2022-05-19):
        #   Intel Family 6:
        #     HASWELL_X (0x3F)
        #     BROADWELL_D (0x56), BROADWELL_X (0x4F)
        #     SKYLAKE_X (0x55), SKYLAKE_L (0x4E), SKYLAKE (0x5E)
        #     KABYLAKE_L (0x8E), KABYLAKE (0x9E)
        #     ICELAKE_L (0x7E), ICELAKE_D (0x6C), ICELAKE_X (0x6A)
        #     COMETLAKE (0xA5), COMETLAKE_L (0xA6)
        #     LAKEFIELD (0x8A)
        #     ROCKETLAKE (0xA7)
        #     ATOM_TREMONT (0x96), ATOM_TREMONT_D (0x86), ATOM_TREMONT_L (0x9C)
        #   All steppings. No stepping restrictions for MMIO flag itself.
        #
        # No models have been added to or removed from the MMIO blacklist since v5.19.
        #
        # 7df548840c49 (v6.0, NO_MMIO whitelist added, Pawan Gupta 2022-08-03):
        #   Intel Family 6:
        #     TIGERLAKE (0x8D), TIGERLAKE_L (0x8C)
        #     ALDERLAKE (0x97), ALDERLAKE_L (0x9A)
        #     ATOM_GOLDMONT (0x5C), ATOM_GOLDMONT_D (0x5F), ATOM_GOLDMONT_PLUS (0x7A)
        #   AMD: fam 0x0f-0x12 + X86_FAMILY_ANY (all families)
        #   Hygon: all families
        #   Centaur fam 7, Zhaoxin fam 7
        #
        # Kernel logic (v6.0+):
        #   if (!arch_cap_mmio_immune(ia32_cap)) {
        #       if (cpu_matches(cpu_vuln_blacklist, MMIO))
        #           setup_force_cpu_bug(X86_BUG_MMIO_STALE_DATA);
        #       else if (!cpu_matches(cpu_vuln_whitelist, NO_MMIO))
        #           setup_force_cpu_bug(X86_BUG_MMIO_UNKNOWN);
        #   }
        #   => Intel CPUs that are neither blacklisted nor whitelisted (e.g. Ivy Bridge,
        #      Haswell client, Broadwell client, Sandy Bridge, pre-Goldmont Atom, etc.) get
        #      X86_BUG_MMIO_UNKNOWN and report "Unknown: No mitigations" in sysfs. Intel
        #      never published an affected-processor evaluation for these models because
        #      their servicing period had already ended.
        #   => is_cpu_mmio_unknown() matches this set so the script can report UNK (or
        #      VULN under --paranoid) rather than the misleading "not affected" that
        #      a plain blacklist check would produce.
        #
        # immunity: ARCH_CAP_SBDR_SSDP_NO (bit 13) AND ARCH_CAP_FBSDP_NO (bit 14) AND ARCH_CAP_PSDP_NO (bit 15)
        #   All three must be set. Checked via arch_cap_mmio_immune() in common.c.
        #
        # microcode mitigation: ARCH_CAP_FB_CLEAR (bit 17) -- VERW clears fill buffers.
        #   Alternative: MD_CLEAR CPUID + FLUSH_L1D CPUID when MDS_NO is not set (legacy path).
        #
        # vendor scope: Intel only. Non-Intel CPUs never set X86_BUG_MMIO_STALE_DATA.
        sys_interface_available=1
        status=$ret_sys_interface_check_status
    fi

    if [ "$opt_sysfs_only" != 1 ]; then
        # MMIO Stale Data is Intel-only; skip x86-specific kernel/MSR checks on non-x86 kernels
        kernel_mmio=''
        kernel_mmio_can_tell=0
        # Whether this kernel implements the X86_BUG_MMIO_UNKNOWN distinction, i.e. can
        # report "Unknown: No mitigations" for CPUs Intel never assessed. Only such kernels
        # emit a *trustworthy* "Not affected": they would have said "Unknown" instead if the
        # CPU were in the unknown bucket. Detected by the presence of the literal sysfs string in the kernel image.
        kernel_mmio_unknown_aware=0
        if is_x86_kernel; then
            pr_info_nol "* Kernel supports MMIO Stale Data mitigation: "
            kernel_mmio_can_tell=1
            if [ -n "$g_kernel_err" ]; then
                kernel_mmio_can_tell=0
            elif grep -q 'mmio_stale_data' "$g_kernel" 2>/dev/null; then
                pr_debug "mmio: found 'mmio_stale_data' string in kernel image"
                kernel_mmio='found MMIO Stale Data mitigation evidence in kernel image'
                pstatus green YES "$kernel_mmio"
            fi
            if [ -z "$g_kernel_err" ] && grep -qF 'Unknown: No mitigations' "$g_kernel" 2>/dev/null; then
                pr_debug "mmio: kernel image knows the 'Unknown: No mitigations' state (X86_BUG_MMIO_UNKNOWN-aware)"
                kernel_mmio_unknown_aware=1
            fi
            if [ -z "$kernel_mmio" ] && [ -n "$opt_config" ] && grep -q '^CONFIG_MITIGATION_MMIO_STALE_DATA=y' "$opt_config"; then
                kernel_mmio='found MMIO Stale Data mitigation config option enabled'
                pstatus green YES "$kernel_mmio"
            fi
            if [ -z "$kernel_mmio" ] && [ -n "$opt_map" ]; then
                if grep -qE 'mmio_select_mitigation|cpu_show_mmio_stale_data' "$opt_map"; then
                    kernel_mmio='found MMIO Stale Data mitigation function in System.map'
                    pstatus green YES "$kernel_mmio"
                fi
            fi
            if [ -z "$kernel_mmio" ]; then
                if [ "$kernel_mmio_can_tell" = 1 ]; then
                    pstatus yellow NO
                else
                    pstatus yellow UNKNOWN
                fi
            fi

            pr_info_nol "* CPU microcode supports Fill Buffer clearing: "
            if [ "$cap_fb_clear" = -1 ]; then
                pstatus yellow UNKNOWN
            elif [ "$cap_fb_clear" = 1 ]; then
                pstatus green YES
            else
                pstatus yellow NO
            fi

            if [ "$g_mode" = live ] && [ "$sys_interface_available" = 1 ]; then
                pr_info_nol "* Kernel mitigation is enabled and active: "
                if echo "$ret_sys_interface_check_fullmsg" | grep -qi ^mitigation; then
                    mmio_mitigated=1
                    pstatus green YES
                else
                    mmio_mitigated=0
                    pstatus yellow NO
                fi
                pr_info_nol "* SMT is either mitigated or disabled: "
                if echo "$ret_sys_interface_check_fullmsg" | grep -Eq 'SMT (disabled|mitigated)'; then
                    mmio_smt_mitigated=1
                    pstatus green YES
                elif echo "$ret_sys_interface_check_fullmsg" | grep -q 'SMT Host state unknown'; then
                    # The kernel appends "SMT Host state unknown" whenever the
                    # HYPERVISOR CPUID bit is set. That's true both inside a guest
                    # AND on a Xen dom0 (#343). In a guest we genuinely can't see
                    # the host's SMT scheduling; on dom0/bare metal the local SMT
                    # state is authoritative, so trust it there.
                    if is_running_as_guest; then
                        mmio_smt_mitigated=2
                        pstatus yellow UNKNOWN "running in a VM guest, the hypervisor host controls SMT"
                    elif is_cpu_smt_enabled; then
                        mmio_smt_mitigated=0
                        pstatus yellow NO
                    else
                        mmio_smt_mitigated=1
                        pstatus green YES
                    fi
                else
                    mmio_smt_mitigated=0
                    pstatus yellow NO
                fi
            fi
        fi # is_x86_kernel
    elif [ "$sys_interface_available" = 0 ]; then
        # we have no sysfs but were asked to use it only!
        msg="/sys vulnerability interface use forced, but it's not available!"
        status=UNK
    fi

    if ! is_cpu_affected "$cve"; then
        # override status & msg in case CPU is not vulnerable after all
        pvulnstatus "$cve" OK "your CPU vendor reported your CPU model as not affected"
    elif [ "$opt_sysfs_only" != 1 ] && is_cpu_mmio_unknown; then
        # Bypass the normal sysfs reconciliation: sysfs reports "Unknown: No mitigations"
        # only on v6.0-v6.15. On earlier and on v6.16+ kernels it wrongly says "Not affected"
        # for these CPUs (which predate FB_CLEAR microcode and Intel's affected-processor list).
        if is_arch_cap_mmio_undetermined; then
            # We landed in the "unknown" bucket only because the IA32_ARCH_CAPABILITIES
            # MSR couldn't be read from userspace (no msr module, or kernel lockdown under
            # Secure Boot): the CPU might actually advertise MMIO immunity
            # through FBSDP_NO/PSDP_NO/SBDR_SSDP_NO, but we can't read it, however the kernel can.
            #
            # We can trust a sysfs "Not affected" only if this kernel is X86_BUG_MMIO_UNKNOWN-aware:
            # such a kernel would have reported "Unknown: No mitigations" instead if the CPU were in
            # the unknown bucket, so "Not affected" genuinely means arch-cap immune.
            # On kernels that lack that distinction, a "Not affected" is not trustworthy for these CPUs,
            # so we keep UNK.
            if [ "$g_mode" = live ] && [ "$sys_interface_available" = 1 ] &&
                [ "$kernel_mmio_unknown_aware" = 1 ] && [ "$status" = OK ]; then
                pvulnstatus "$cve" OK "your kernel reports your CPU as not affected, and this kernel distinguishes the MMIO 'unknown' state, so its verdict is trustworthy (we couldn't read the IA32_ARCH_CAPABILITIES MSR ourselves)"
            else
                unk="your CPU's MMIO Stale Data status could not be determined: the IA32_ARCH_CAPABILITIES MSR (0x10a) couldn't be read"
                pvulnstatus "$cve" UNK "$unk; load the msr module and/or disable kernel lockdown, then re-run as root to get a definitive answer"
            fi
        else
            unk="your CPU's MMIO Stale Data status is unknown (Intel never officially assessed this CPU, its servicing period has ended)"
            if [ "$opt_paranoid" = 1 ]; then
                pvulnstatus "$cve" VULN "$unk, and no mitigation is available"
                explain "There is no known mitigation for this CPU model. Intel ended its servicing period without evaluating whether it is affected by MMIO Stale Data vulnerabilities, so no FB_CLEAR-capable microcode was released."
            else
                pvulnstatus "$cve" UNK "$unk; no mitigation is available in any case"
            fi
        fi
    else
        if [ "$opt_sysfs_only" != 1 ]; then
            # compute mystatus and mymsg from our own logic
            if [ "$cap_fb_clear" = 1 ]; then
                if [ -n "$kernel_mmio" ]; then
                    if [ "$g_mode" = live ]; then
                        # mitigation must also be enabled
                        if [ "$mmio_mitigated" = 1 ]; then
                            if [ "$opt_paranoid" != 1 ] || [ "$mmio_smt_mitigated" = 1 ]; then
                                mystatus=OK
                                mymsg="Your microcode and kernel are both up to date for this mitigation, and mitigation is enabled"
                            elif [ "$mmio_smt_mitigated" = 2 ]; then
                                mystatus=UNK
                                mymsg="Your microcode and kernel are both up to date for this mitigation and it's enabled, but SMT (Hyper-Threading) cross-thread protection can't be verified from inside a VM guest: it depends on the hypervisor host's SMT/core-scheduling configuration"
                            else
                                mystatus=VULN
                                mymsg="Your microcode and kernel are both up to date for this mitigation, but you must disable SMT (Hyper-Threading) for a complete mitigation"
                            fi
                        else
                            mystatus=VULN
                            mymsg="Your microcode and kernel are both up to date for this mitigation, but the mitigation is not active"
                        fi
                    else
                        mystatus=OK
                        mymsg="Your microcode and kernel are both up to date for this mitigation"
                    fi
                else
                    mystatus=VULN
                    mymsg="Your microcode supports mitigation, but your kernel doesn't, upgrade it to mitigate the vulnerability"
                fi
            else
                if [ -n "$kernel_mmio" ]; then
                    mystatus=VULN
                    mymsg="Your kernel supports mitigation, but your CPU microcode also needs to be updated to mitigate the vulnerability"
                else
                    mystatus=VULN
                    mymsg="Neither your kernel or your microcode support mitigation, upgrade both to mitigate the vulnerability"
                fi
            fi
        else
            # sysfs only: return the status/msg we got
            pvulnstatus "$cve" "$status" "$ret_sys_interface_check_fullmsg"
            return
        fi

        # if we didn't get a msg+status from sysfs, use ours
        if [ -z "$msg" ]; then
            pvulnstatus "$cve" "$mystatus" "$mymsg"
        elif [ "$opt_paranoid" = 1 ]; then
            # if paranoid mode is enabled, we know that we won't agree on status, so take ours
            pvulnstatus "$cve" "$mystatus" "$mymsg"
        elif [ "$status" = "$mystatus" ]; then
            # if we agree on status, we'll print the common status and our message (more detailed than the sysfs one)
            pvulnstatus "$cve" "$status" "$mymsg"
        else
            # if we don't agree on status, maybe our logic is flawed due to a new kernel/mitigation? use the one from sysfs
            pvulnstatus "$cve" "$status" "$msg"
        fi

        if [ "$mystatus" = VULN ]; then
            explain "Update your kernel to a version that includes MMIO Stale Data mitigation (Linux 5.19+), and update your CPU microcode. If you are using a distribution kernel, make sure you are up to date. To enforce full mitigation including SMT, boot with 'mmio_stale_data=full,nosmt'."
        fi
    fi
}
