/*
 * 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 2023 Oxide Computer Company
 */

/*
 * A collection of utility functions for interacting with fabric IDs.
 */

#include <amdzen_client.h>

/*
 * Validate whether a fabric ID actually represents a valid ID for a given data
 * fabric.
 */
boolean_t
zen_fabric_id_valid_fabid(const df_fabric_decomp_t *decomp,
    const uint32_t fabid)
{
	uint32_t mask = decomp->dfd_node_mask | decomp->dfd_comp_mask;
	return ((fabid & ~mask) == 0);
}

/*
 * Validate whether the parts of a fabric ID (e.g. the socket, die, and
 * component) are in fact valid for a given data fabric.
 */
boolean_t
zen_fabric_id_valid_parts(const df_fabric_decomp_t *decomp, const uint32_t sock,
    const uint32_t die, const uint32_t comp)
{
	uint32_t node;

	if (((sock << decomp->dfd_sock_shift) & ~decomp->dfd_sock_mask) != 0) {
		return (B_FALSE);
	}
	if (((die << decomp->dfd_die_shift) & ~decomp->dfd_die_mask) != 0) {
		return (B_FALSE);
	}
	if ((comp & ~decomp->dfd_comp_mask) != 0) {
		return (B_FALSE);
	}

	node = die << decomp->dfd_die_shift;
	node |= sock << decomp->dfd_sock_shift;

	if (((node << decomp->dfd_node_shift) & ~decomp->dfd_node_mask) != 0) {
		return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Take apart a fabric ID into its constituent parts. The decomposition
 * information has the die and socket information relative to the node ID.
 */
void
zen_fabric_id_decompose(const df_fabric_decomp_t *decomp, const uint32_t fabid,
    uint32_t *sockp, uint32_t *diep, uint32_t *compp)
{
	uint32_t node;

	ASSERT(zen_fabric_id_valid_fabid(decomp, fabid));

	*compp = (fabid & decomp->dfd_comp_mask) >> decomp->dfd_comp_shift;
	node = (fabid & decomp->dfd_node_mask) >> decomp->dfd_node_shift;
	*diep = (node & decomp->dfd_die_mask) >> decomp->dfd_die_shift;
	*sockp = (node & decomp->dfd_sock_mask) >> decomp->dfd_sock_shift;
}

/*
 * Compose a fabric ID from its constituent parts: the socket, die, and fabric.
 */
void
zen_fabric_id_compose(const df_fabric_decomp_t *decomp, const uint32_t sock,
    const uint32_t die, const uint32_t comp, uint32_t *fabidp)
{
	uint32_t node;

	ASSERT(zen_fabric_id_valid_parts(decomp, sock, die, comp));

	node = die << decomp->dfd_die_shift;
	node |= sock << decomp->dfd_sock_shift;
	*fabidp = (node << decomp->dfd_node_shift) |
	    (comp << decomp->dfd_comp_shift);
}

#ifdef	DEBUG
static boolean_t
zen_apic_id_valid_parts(const amdzen_apic_decomp_t *decomp, const uint32_t sock,
    const uint32_t die, const uint32_t ccd, const uint32_t ccx,
    const uint32_t core, const uint32_t thread)
{
	ASSERT3U(decomp->aad_sock_shift, <, 32);
	ASSERT3U(decomp->aad_die_shift, <, 32);
	ASSERT3U(decomp->aad_ccd_shift, <, 32);
	ASSERT3U(decomp->aad_ccx_shift, <, 32);
	ASSERT3U(decomp->aad_core_shift, <, 32);
	ASSERT3U(decomp->aad_thread_shift, <, 32);

	if (((sock << decomp->aad_sock_shift) & ~decomp->aad_sock_mask) != 0) {
		return (B_FALSE);
	}

	if (((die << decomp->aad_die_shift) & ~decomp->aad_die_mask) != 0) {
		return (B_FALSE);
	}

	if (((ccd << decomp->aad_ccd_shift) & ~decomp->aad_ccd_mask) != 0) {
		return (B_FALSE);
	}

	if (((ccx << decomp->aad_ccx_shift) & ~decomp->aad_ccx_mask) != 0) {
		return (B_FALSE);
	}

	if (((core << decomp->aad_core_shift) & ~decomp->aad_core_mask) != 0) {
		return (B_FALSE);
	}

	if (((thread << decomp->aad_thread_shift) &
	    ~decomp->aad_thread_mask) != 0) {
		return (B_FALSE);
	}
	return (B_TRUE);
}
#endif	/* DEBUG */

/*
 * Compose an APIC ID from its constituent parts.
 */
void
zen_apic_id_compose(const amdzen_apic_decomp_t *decomp, const uint32_t sock,
    const uint32_t die, const uint32_t ccd, const uint32_t ccx,
    const uint32_t core, const uint32_t thread, uint32_t *apicid)
{
	uint32_t id;

	ASSERT(zen_apic_id_valid_parts(decomp, sock, die, ccd, ccx, core,
	    thread));
	id = thread << decomp->aad_thread_shift;
	id |= core << decomp->aad_core_shift;
	id |= ccx << decomp->aad_ccx_shift;
	id |= ccd << decomp->aad_ccd_shift;
	id |= die << decomp->aad_die_shift;
	id |= sock << decomp->aad_sock_shift;

	*apicid = id;
}