root/tools/testing/selftests/powerpc/dexcr/lsdexcr.c
// SPDX-License-Identifier: GPL-2.0+

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>

#include "dexcr.h"
#include "utils.h"

static unsigned int dexcr;
static unsigned int hdexcr;
static unsigned int effective;

static void print_list(const char *list[], size_t len)
{
        for (size_t i = 0; i < len; i++) {
                printf("%s", list[i]);
                if (i + 1 < len)
                        printf(", ");
        }
}

static void print_dexcr(char *name, unsigned int bits)
{
        const char *enabled_aspects[ARRAY_SIZE(aspects) + 1] = {NULL};
        size_t j = 0;

        printf("%s: 0x%08x", name, bits);

        if (bits == 0) {
                printf("\n");
                return;
        }

        for (size_t i = 0; i < ARRAY_SIZE(aspects); i++) {
                unsigned int mask = DEXCR_PR_BIT(aspects[i].index);

                if (bits & mask) {
                        enabled_aspects[j++] = aspects[i].name;
                        bits &= ~mask;
                }
        }

        if (bits)
                enabled_aspects[j++] = "unknown";

        printf(" (");
        print_list(enabled_aspects, j);
        printf(")\n");
}

static void print_aspect(const struct dexcr_aspect *aspect)
{
        const char *attributes[8] = {NULL};
        size_t j = 0;
        unsigned long mask;

        mask = DEXCR_PR_BIT(aspect->index);
        if (dexcr & mask)
                attributes[j++] = "set";
        if (hdexcr & mask)
                attributes[j++] = "set (hypervisor)";
        if (!(effective & mask))
                attributes[j++] = "clear";

        printf("%12s %c (%d): ", aspect->name, effective & mask ? '*' : ' ', aspect->index);
        print_list(attributes, j);
        printf("  \t(%s)\n", aspect->desc);
}

static void print_aspect_config(const struct dexcr_aspect *aspect)
{
        const char *reason = NULL;
        const char *reason_hyp = NULL;
        const char *reason_prctl = "no prctl";
        bool actual = effective & DEXCR_PR_BIT(aspect->index);
        bool expected = actual;  /* Assume it's fine if we don't expect a specific set/clear value */

        if (actual)
                reason = "set by unknown";
        else
                reason = "cleared by unknown";

        if (aspect->prctl != -1) {
                int ctrl = pr_get_dexcr(aspect->prctl);

                if (ctrl < 0) {
                        reason_prctl = "failed to read prctl";
                } else {
                        if (ctrl & PR_PPC_DEXCR_CTRL_SET) {
                                reason_prctl = "set by prctl";
                                expected = true;
                        } else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR) {
                                reason_prctl = "cleared by prctl";
                                expected = false;
                        } else {
                                reason_prctl = "unknown prctl";
                        }

                        reason = reason_prctl;
                }
        }

        if (hdexcr & DEXCR_PR_BIT(aspect->index)) {
                reason_hyp = "set by hypervisor";
                reason = reason_hyp;
                expected = true;
        } else {
                reason_hyp = "not modified by hypervisor";
        }

        printf("%12s (%d): %-28s (%s, %s)\n",
               aspect->name,
               aspect->index,
               reason,
               reason_hyp,
               reason_prctl);

        /*
         * The checks are not atomic, so this can technically trigger if the
         * hypervisor makes a change while we are checking each source. It's
         * far more likely to be a bug if we see this though.
         */
        if (actual != expected)
                printf("                : ! actual %s does not match config\n", aspect->name);
}

int main(int argc, char *argv[])
{
        if (!dexcr_exists()) {
                printf("DEXCR not detected on this hardware\n");
                return 1;
        }

        dexcr = get_dexcr(DEXCR);
        hdexcr = get_dexcr(HDEXCR);
        effective = dexcr | hdexcr;

        printf("current status:\n");

        print_dexcr("    DEXCR", dexcr);
        print_dexcr("   HDEXCR", hdexcr);
        print_dexcr("Effective", effective);
        printf("\n");

        for (size_t i = 0; i < ARRAY_SIZE(aspects); i++)
                print_aspect(&aspects[i]);
        printf("\n");

        if (effective & DEXCR_PR_NPHIE) {
                printf("DEXCR[NPHIE] enabled: hashst/hashchk ");
                if (hashchk_triggers())
                        printf("working\n");
                else
                        printf("failed to trigger\n");
        } else {
                printf("DEXCR[NPHIE] disabled: hashst/hashchk ");
                if (hashchk_triggers())
                        printf("unexpectedly triggered\n");
                else
                        printf("ignored\n");
        }
        printf("\n");

        printf("configuration:\n");
        for (size_t i = 0; i < ARRAY_SIZE(aspects); i++)
                print_aspect_config(&aspects[i]);
        printf("\n");

        return 0;
}