// SPDX-License-Identifier: GPL-2.0+

#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

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

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

struct dexcr_aspect {
	const char *name;
	const char *desc;
	unsigned int index;
};

static const struct dexcr_aspect aspects[] = {
	{
		.name = "SBHE",
		.desc = "Speculative branch hint enable",
		.index = 0,
	},
	{
		.name = "IBRTPD",
		.desc = "Indirect branch recurrent target prediction disable",
		.index = 3,
	},
	{
		.name = "SRAPD",
		.desc = "Subroutine return address prediction disable",
		.index = 4,
	},
	{
		.name = "NPHIE",
		.desc = "Non-privileged hash instruction enable",
		.index = 5,
	},
	{
		.name = "PHIE",
		.desc = "Privileged hash instruction enable",
		.index = 6,
	},
};

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: %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);
}

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;

	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");
	}

	return 0;
}