root/usr/src/test/bhyve-tests/tests/kdev/payload_vlapic_msr_access.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2022 Oxide Computer Company
 */

#include "payload_common.h"
#include "payload_utils.h"
#include "test_defs.h"

#define MSR_APICBASE    0x1b
#define MSR_X2APIC_BASE 0x800
#define MSR_X2APIC_MAX  0x8ff

#define APICBASE_X2APIC (1 << 10)

static bool
reg_readable(uint32_t reg)
{
        switch (reg) {
        case 0x802: /* ID */
        case 0x803: /* VER */

        case 0x808: /* TPR */
        case 0x809: /* APR */
        case 0x80a: /* PPR */

        case 0x80c: /* RRR */
        case 0x80d: /* LDR */
        case 0x80e: /* DFR */
        case 0x80f: /* SVR */

        case 0x810 ... 0x817: /* ISR */
        case 0x818 ... 0x81f: /* TMR */
        case 0x820 ... 0x827: /* IRR */

        case 0x828: /* ESR */

        case 0x82f: /* LVT_CMCI */
        case 0x830: /* ICR */

        case 0x832: /* LVT_TIMER */
        case 0x833: /* LVT_THERMAL */
        case 0x834: /* LVT_PERF */
        case 0x835: /* LVT_LINT0 */
        case 0x836: /* LVT_LINT1 */
        case 0x837: /* LVT_ERROR */
        case 0x838: /* TIMER_ICR */
        case 0x839: /* TIMER_CCR */

        case 0x83e: /* TIMER_DCR */
                return (true);
        default:
                return (false);
        }
}

static bool
reg_writable(uint32_t reg)
{
        switch (reg) {
        case 0x802: /* ID */

        case 0x808: /* TPR */

        case 0x80b: /* EOI */

        case 0x80d: /* LDR */
        case 0x80e: /* DFR */
        case 0x80f: /* SVR */

        case 0x828: /* ESR */

        case 0x82f: /* LVT_CMCI */
        case 0x830: /* ICR */

        case 0x832: /* LVT_TIMER */
        case 0x833: /* LVT_THERMAL */
        case 0x834: /* LVT_PERF */
        case 0x835: /* LVT_LINT0 */
        case 0x836: /* LVT_LINT1 */
        case 0x837: /* LVT_ERROR */
        case 0x838: /* TIMER_ICR */

        case 0x83e: /* TIMER_DCR */
        case 0x83f: /* SELF_IPI */
                return (true);
        default:
                return (false);
        }
}

void
start(void)
{
        uint64_t base = rdmsr(MSR_APICBASE);
        if ((base & APICBASE_X2APIC) == 0) {
                /* bail if the host has not enabled x2apic for us */
                outb(IOP_TEST_RESULT, TEST_RESULT_FAIL);
        }

        for (uint32_t msr = MSR_X2APIC_BASE; msr <= MSR_X2APIC_MAX; msr++) {
                uint64_t val = 0;

                if (reg_readable(msr)) {
                        val = rdmsr(msr);
                }

                if (reg_writable(msr)) {
                        if (msr == 0x828) {
                                /*
                                 * While the LAPIC is in x2APIC mode, writes to
                                 * the ESR must carry a value of 0.
                                 */
                                val = 0;
                        }
                        wrmsr(msr, val);
                }
        }

        /* If we made it this far without a #GP, it counts as a win */
        outb(IOP_TEST_RESULT, TEST_RESULT_PASS);
}