/**  @file addrxlat.h
 * Public interface for `libaddrxlat` (address translation library).
*/
/* Copyright (C) 2016 Petr Tesarik <ptesarik@suse.com>

   This file is free software; you can redistribute it and/or modify
   it under the terms of either

     * the GNU Lesser General Public License as published by the Free
       Software Foundation; either version 3 of the License, or (at
       your option) any later version

   or

     * the GNU General Public License as published by the Free
       Software Foundation; either version 2 of the License, or (at
       your option) any later version

   or both in parallel, as here.

   libkdumpfile is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received copies of the GNU General Public License and
   the GNU Lesser General Public License along with this program.  If
   not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _ADDRXLAT_H
#define _ADDRXLAT_H	1

#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>

/** Major version (1st number in the release tag). */
#define ADDRXLAT_VER_MAJOR	0
/** Minor version (2nd number in the release tag). */
#define ADDRXLAT_VER_MINOR	5
/** Micro version (3rd number in the release tag). */
#define ADDRXLAT_VER_MICRO	5

/** Make a single-number version from three digits of the release tag. */
#define ADDRXLAT_MKVER(major,minor,micro)	\
	(((major) << 16) | ((minor) << 8) | ((micro)))

/** Version as a single number */
#define ADDRXLAT_VERSION		\
	ADDRXLAT_MKVER(			\
		ADDRXLAT_VER_MAJOR,	\
		ADDRXLAT_VER_MINOR,	\
		ADDRXLAT_VER_MICRO)

/** Version as a string constant */
#define ADDRXLAT_VERSION_STRING "0.5.5"

#ifdef  __cplusplus
extern "C" {
#endif

/**  Status code.
 *
 * Positive codes are reserved for future library enhancements. Negative
 * status codes may be used for custom use.
 */
typedef enum _addrxlat_status {
	ADDRXLAT_OK = 0,		/**< Success. */
	ADDRXLAT_ERR_NOTIMPL,		/**< Unimplemented feature. */
	ADDRXLAT_ERR_NOTPRESENT,	/**< Page not present. */
	ADDRXLAT_ERR_INVALID,		/**< Invalid address. */
	ADDRXLAT_ERR_NOMEM,		/**< Memory allocation failure. */
	ADDRXLAT_ERR_NODATA,		/**< Data not available. */
	ADDRXLAT_ERR_NOMETH,		/**< No translation method. */

	/** Base for custom status codes.
	 * More importantly, this enumerator forces the whole enum
	 * type to be signed, so you can compare it against 0 without
	 * an explicit typecast to a signed integer.
	 */
	ADDRXLAT_ERR_CUSTOM_BASE = -1
} addrxlat_status;

/**  Return the string describing a given error status.
 * @param status  Error status.
 * @returns       Description of the error.
 */
const char *addrxlat_strerror(addrxlat_status status);

/**  Type of a physical or virtual address.
 *
 * This type is large enough to hold any possible address type on any
 * architecture supported by `libaddrxlat`. Note that this type may
 * be larger than the actual address in the target.
 */
typedef uint_fast64_t addrxlat_addr_t;

/**  Maximum value that can be represented by addrxlat_addr_t.
 */
#define ADDRXLAT_ADDR_MAX	(~(addrxlat_addr_t)0)

/**  Type of an address offset.
 *
 * This type is as large as @ref addrxlat_addr_t, but it is signed,
 * so suitable for offsets, both positive and negative.
 */
typedef int_fast64_t addrxlat_off_t;

/**  Type for a PTE value.
 *
 * Use this type to work with PTE values. Note that this type may be
 * bigger than the actual PTE on a given architecture and always uses
 * host byte order, so variables of this type are not suitable for use
 * as a buffer.
 */
typedef uint_fast64_t addrxlat_pte_t;

/** @name fprintf() macros for addrxlat types
 * @{
 *
 * These macros are similar to POSIX @c PRI_xxx macros. Each of these
 * macros expands to a character string literal containing a conversion
 * specifier, possibly modified by a length modifier, suitable for use
 * within the format argument of a formatted input/output function when
 * converting the corresponding integer type.
 */
#define ADDRXLAT_PRIoADDR	PRIoFAST64 /**< Octal address */
#define ADDRXLAT_PRIuADDR	PRIuFAST64 /**< Decimal address */
#define ADDRXLAT_PRIxADDR	PRIxFAST64 /**< Lowercase hex address */
#define ADDRXLAT_PRIXADDR	PRIXFAST64 /**< Uppercase hex address */

#define ADDRXLAT_PRIoPTE	PRIoFAST64 /**< Octal PTE */
#define ADDRXLAT_PRIuPTE	PRIuFAST64 /**< Decimal PTE */
#define ADDRXLAT_PRIxPTE	PRIxFAST64 /**< Lowercase hex PTE */
#define ADDRXLAT_PRIXPTE	PRIXFAST64 /**< Uppercase hex PTE */
/* @} */

/**  Address spaces
 *
 * This type is used to specify the kind of address.
 *
 * The difference between @c ADDRXLAT_KPHYSADDR and @c ADDRXLAT_MACHPHYSADDR
 * matters only in environments where the kernel has a different view
 * of physical address space than the CPU, e.g. paravirtualized kernels
 * under Xen.
 */
typedef enum _addrxlat_addrspace {
	ADDRXLAT_KPHYSADDR,	/**< Kernel physical address. */
	ADDRXLAT_MACHPHYSADDR,	/**< Machine physical address. */
	ADDRXLAT_KVADDR,	/**< Kernel virtual address. */

	ADDRXLAT_NOADDR = -1,	/**< Invalid address. */
} addrxlat_addrspace_t;

/** Get the (string) name of an address space.
 * @param as  Address space.
 * @returns   The (human-readable) name of the address space.
 */
const char *addrxlat_addrspace_name(addrxlat_addrspace_t as);

/** Full address (including address space specification).
 */
typedef struct _addrxlat_fulladdr {
	addrxlat_addr_t addr;	 /**< Raw address. */
	addrxlat_addrspace_t as; /**< Address space for @c addr. */
} addrxlat_fulladdr_t;

typedef struct _addrxlat_ctx addrxlat_ctx_t;

/** Allocate and initialize a new address translation context.
 * @returns    New initialized context, or @c NULL on failure.
 *
 * This call can fail if and only if memory allocation fails.
 * The reference count of the newly created object is one.
 */
addrxlat_ctx_t *addrxlat_ctx_new(void);

/** Increment the reference counter.
 * @param ctx  Address translation context.
 * @returns    New reference count.
 */
unsigned long addrxlat_ctx_incref(addrxlat_ctx_t *ctx);

/** Decrement the reference counter.
 * @param ctx  Address translation context.
 * @returns    New reference count.
 *
 * If the new reference count is zero, the underlying object is freed
 * and its address must not be used afterwards.
 */
unsigned long addrxlat_ctx_decref(addrxlat_ctx_t *ctx);

/** Prepend an error message.
 * @param ctx     Address translation context.
 * @param status  Error status.
 * @param msgfmt  Message format string (@c printf style).
 * @returns       The error status which was passed as argument.
 *
 * This function prepends the new error message to the existing
 * content of the error buffer, resulting in a kind of error
 * backtrace.
 */
addrxlat_status addrxlat_ctx_err(
	addrxlat_ctx_t *ctx, addrxlat_status status,
	const char *msgfmt, ...)
	__attribute__ ((format (printf, 3, 4)));

/** Clear the error message.
 * @param ctx  Address translation context.
 */
void addrxlat_ctx_clear_err(addrxlat_ctx_t *ctx);

/**  Get a detailed error string.
 * @param ctx  Address translation context.
 * @returns    Last error string.
 *
 * If an error status is returned, this function can be used to get
 * a human-readable description of the error. The error string is not
 * reset by calling this function, but it is reset by calling any
 * library function that returns @ref addrxlat_status.
 */
const char *addrxlat_ctx_get_err(const addrxlat_ctx_t *ctx);

/* Forward declaration to solve circular reference. */
typedef struct _addrxlat_cb addrxlat_cb_t;

/** Type of the register value callback.
 * @param cb        This callback definition.
 * @param name[in]  Register name.
 * @param val[out]  Register value.
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_cb_reg_value_fn(
	const addrxlat_cb_t *cb, const char *name, addrxlat_addr_t *val);

/** Type of the symbol value callback.
 * @param cb        This callback definition.
 * @param name[in]  Symbol name.
 * @param val[out]  Symbol value (e.g. address of a variable).
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_cb_sym_value_fn(
	const addrxlat_cb_t *cb, const char *name, addrxlat_addr_t *val);

/** Type of the symbol size callback.
 * @param cb        This callback definition.
 * @param name[in]  Name of a symbol or data type.
 * @param val[out]  Symbol size, @c sizeof(name).
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_cb_sym_sizeof_fn(
	const addrxlat_cb_t *cb, const char *name, addrxlat_addr_t *val);

/** Type of the element offset callback.
 * @param cb        This callback definition.
 * @param obj[in]   Name of the container object (e.g. of a @c struct).
 * @param elem[in]  Name of the element (e.g. a structure field).
 * @param val[out]  Offset of @p elem within @p obj, @c offsetof(obj, elem).
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_cb_sym_offsetof_fn(
	const addrxlat_cb_t *cb, const char *obj, const char *elem,
	addrxlat_addr_t *val);

/** Type of the number value callback.
 * @param cb        This callback definition.
 * @param name[in]  Name of the numeric parameter.
 * @param val[out]  Parameter value.
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_cb_num_value_fn(
	const addrxlat_cb_t *cb, const char *name, addrxlat_addr_t *val);

/**  Data byte order.
 *
 * Byte order of the raw data returned by callbacks.
 */
typedef enum _addrxlat_byte_order {
	ADDRXLAT_BIG_ENDIAN,	/**< Big-endian buffers. */
	ADDRXLAT_LITTLE_ENDIAN,	/**< Little-endian buffers. */

	/** Host-endian buffers. In other words, buffer data can
	 *  be used directly without any conversion.
	 */
	ADDRXLAT_HOST_ENDIAN = -1
} addrxlat_byte_order_t;

/** Read buffer metadata. */
typedef struct _addrxlat_buffer addrxlat_buffer_t;

/** Type of the put-page callback.
 * @param[in] buf   Page buffer metadata.
 */
typedef void addrxlat_put_page_fn(const addrxlat_buffer_t *buf);

/** Read buffer metadata.
 *
 * This structure is used to pass data between the library and a
 * callback function. The idea is that the library should not have
 * to care about the limitations of any given implementation. It
 * communicates the intention to get data at a given address, the
 * get-page callback translates that to a full page and returns as
 * much as possible, adjusting remaining fields accordingly.
 *
 * NOTE: The callback may theoretically provide as little as one
 * byte at the desired address. However, it does not happen in
 * practice, because:
 * - the library needs at most 64-bit entities (8 bytes),
 * - a page is greater than 8 bytes on all architectures,
 * - the desired address is always aligned.
 * The library does NOT implement reads across page boundary.
 * Keep that in mind when writing your own custom implementation.
 */
struct _addrxlat_buffer {
	/** Buffer start address.
	 * This field is set to the desired read address before calling
	 * the get-page callback. The callback should adjust it to the
	 * start address of the page that contains the desired address.
	 **/
	addrxlat_fulladdr_t addr;

	/** Pointer to raw binary data. */
	const void *ptr;

	/** Buffer size.
	 * This field must be initialized by the get-page callback to
	 * the length of the buffer at @c ptr.
	 */
	size_t size;

	/** Page data byte order. */
	addrxlat_byte_order_t byte_order;

	/** Callback to release a page buffer. */
	addrxlat_put_page_fn *put_page;

	/** Private data to be used by the callback.
	 * The library will preserve this value between a call
	 * to the get-page and put-page callbacks.  */
	void *priv;
};

/** Type of the get-page callback.
 * @param cb           This callback definition.
 * @param[in,out] buf  Page buffer metadata.
 * @returns            Error status.
 */
typedef addrxlat_status addrxlat_get_page_fn(
	const addrxlat_cb_t *cb, addrxlat_buffer_t *buf);

/** Type of the read capabilities callback.
 * @param cb  This callback definition.
 * @returns   Read capabilities bit mask.
 *
 * The return value is a bit mask of address spaces that can be
 * read directly by the read callbacks. Bits are enumerated by
 * @ref addrxlat_addrspace_t (least significant bit first).
 * @sa ADDRXLAT_CAPS
 */
typedef unsigned long addrxlat_read_caps_fn(const addrxlat_cb_t *cb);

/** Translate an enum value into a capability bitmask. */
#define ADDRXLAT_CAPS(val)	(1UL << (unsigned)(val))

/** Callback function pointers and data. */
struct _addrxlat_cb {
	/** Next chained callback definitions. */
	const addrxlat_cb_t *next;

	/** Private data to be used by the callback.
	 * This field is reserved for use by the callbacks.
	 * The library will not touch it.  */
	void *priv;

	/** Callback to get a page buffer. */
	addrxlat_get_page_fn *get_page;

	/** Read capabilities callback. */
	addrxlat_read_caps_fn *read_caps;

	/** Register value callback. */
	addrxlat_cb_reg_value_fn *reg_value;

	/** Symbol value callback. */
	addrxlat_cb_sym_value_fn *sym_value;

	/** Symbol size callback. */
	addrxlat_cb_sym_sizeof_fn *sym_sizeof;

	/** Element offset callback. */
	addrxlat_cb_sym_offsetof_fn *sym_offsetof;

	/** Number value callback. */
	addrxlat_cb_num_value_fn *num_value;
};

/** Add a callback implementation.
 * @param ctx  Address translation context.
 * @returns    New callback settings, or @c NULL on allocation error.
 *
 * The caller should override the callback settings in the returned
 * structure as necessary. Callbacks that are not modified will use
 * the previously installed implementation.
 */
addrxlat_cb_t *addrxlat_ctx_add_cb(addrxlat_ctx_t *ctx);

/** Remove a callback implementation.
 * @param ctx  Address translation context.
 * @param cb   Callback settings previously returned by
 *             @ref addrxlat_ctx_add_cb.
 *
 * The @p cb parameter is invalid and must not be used after
 * calling this function. It is an error to call this function
 * twice on the same value.
 * It is also an error to pass a pointer that was not obtained
 * by calling @ref addrxlat_ctx_add_cb.
 */
void addrxlat_ctx_del_cb(addrxlat_ctx_t *ctx, addrxlat_cb_t *cb);

/** Get the callback information.
 * @param ctx  Address translation context.
 * @returns    Pointer to the current callback settings.
 *
 * The return value is @c const to indicate that it should not be modified
 * by the caller, but the value can be modified non-atomically by another
 * caller who obtained the pointer through @ref addrxlat_ctx_add_cb.
 */
const addrxlat_cb_t *addrxlat_ctx_get_cb(const addrxlat_ctx_t *ctx);

/** Address translation kind.
 */
typedef enum _addrxlat_kind {
	/** No mapping set. */
	ADDRXLAT_NOMETH,

	/** User-provided callback functions. */
	ADDRXLAT_CUSTOM,

	/** Linear mapping: dest = src + off. */
	ADDRXLAT_LINEAR,

	/** Page table walk. */
	ADDRXLAT_PGT,

	/** Table lookup. */
	ADDRXLAT_LOOKUP,

	/** Array in target memory. */
	ADDRXLAT_MEMARR,
} addrxlat_kind_t;

/* Forward declaration to solve circular reference. */
typedef struct _addrxlat_step addrxlat_step_t;

/** Type of function to initialize the first translation step.
 * @param step  Partially initialized step state.
 * @param addr  Address to be translated.
 * @returns     Error status.
 *
 * Only the context fields (@c ctx, @c sys and @c meth) are set by the
 * caller. The state fields are uninitialized.
 */
typedef addrxlat_status addrxlat_first_step_fn(
	addrxlat_step_t *step, addrxlat_addr_t addr);

/** Type of function to make one translation step.
 * @param step  Current step state.
 * @returns     Error status.
 *
 * This function is called repeatedly with a non-zero @c step->remain.
 *
 * Note that page offset is automatically added by @ref addrxlat_step
 * when @c step->remain is 1. The callback function is never called to
 * do this final step.
 *
 * The callback function is explicitly allowed to modify @c step->remain
 * and/or the indices in @c step->idx[]. This is needed if some levels
 * of paging are skipped (huge pages).
 */
typedef addrxlat_status addrxlat_next_step_fn(addrxlat_step_t *step);

/** Parameters of custom translation. */
typedef struct _addrxlat_param_custom {
	/** Function to perform the initial translation step. */
	addrxlat_first_step_fn *first_step;

	/** Function to perform further translation steps. */
	addrxlat_next_step_fn *next_step;

	/** Arbitrary user-supplied data. */
	void *data;
} addrxlat_param_custom_t;

/** Parameters of linear translation. */
typedef struct _addrxlat_param_linear {
	addrxlat_off_t off;	/**< Address offset. */
} addrxlat_param_linear_t;

/** Page table entry format.
 */
typedef enum _addrxlat_pte_format {
	/** Invalid PTE format value.
	 * It may be returned by the library on error.
	 * Use @c ADDRXLAT_PTE_NONE if the format is merely unknown.
	 */
	ADDRXLAT_PTE_INVALID = -1,

	ADDRXLAT_PTE_NONE,	/**< Undefined. */
	ADDRXLAT_PTE_PFN32,	/**< 32-bit page frame number. */
	ADDRXLAT_PTE_PFN64,	/**< 64-bit page frame number. */
	ADDRXLAT_PTE_AARCH64,	/**< Arm AArch64. */
	ADDRXLAT_PTE_IA32,	/**< Original 32-bit Intel. */
	ADDRXLAT_PTE_IA32_PAE,	/**< Intel IA32 with PAE. */
	ADDRXLAT_PTE_X86_64,	/**< AMD64 (Intel 64).  */
	ADDRXLAT_PTE_S390X,	/**< IBM z/Architecture (64-bit). */

	/** IBM POWER (64-bit) running Linux with RPN shift 30 (64k pages). */
	ADDRXLAT_PTE_PPC64_LINUX_RPN30,

	/** Arm AArch64 with LPA (64k pages). */
	ADDRXLAT_PTE_AARCH64_LPA,

	/** Arm AArch64 with LPA2 (4k or 16k pages). */
	ADDRXLAT_PTE_AARCH64_LPA2,

	ADDRXLAT_PTE_ARM,	/**< Arm AArch32 with short descriptors. */

	/** RISC-V Sv32 (32-bit virtual address) */
	ADDRXLAT_PTE_RISCV32,

	/** RISC-V Sv39, Sv48, Sv57 (64-bit virtual address) */
	ADDRXLAT_PTE_RISCV64,
} addrxlat_pte_format_t;

/** Get the (string) name of a PTE format.
 * @param fmt  PTE format.
 * @returns    The (human-readable) name of the address space,
 *             or @c NULL if unknown.
 */
const char *addrxlat_pte_format_name(addrxlat_pte_format_t fmt);

/** Get a PTE format enum value by name.
 * @param name  PTE name.
 * @returns     PTE format, or @c ADDRXLAT_PTE_INVALID if @p name is
 *              not recognized.
 *
 * The name is not case-sensitive, e.g. "AARCH64", "AArch64" and "aarch64"
 * are handled the same.
 */
addrxlat_pte_format_t addrxlat_pte_format(const char *name);

/** Get the pteval shift for a PTE format.
 * @param fmt  PTE format.
 * @returns    Log2 value of the PTE size, or -1 if unknown / invalid.
 */
int addrxlat_pteval_shift(addrxlat_pte_format_t fmt);

/** Maximum number of bit fields in the source address.
 * This is a theoretical limit, with enough reserve for future enhancements.
 * Currently, IBM z/Architecture has up to 5-level paging (i.e. up to 6 fields
 * in the virtual address), but only 4 are used by the Linux kernel. All other
 * architectures have less paging levels.
 */
#define ADDRXLAT_FIELDS_MAX	8

typedef struct _addrxlat_paging_form {
	/** Format of each page table entry. */
	addrxlat_pte_format_t pte_format;

	/** Number of bit fields in the source address.
	 * With hierarchical page tables, a virtual address is split
	 * into multiple fields, e.g. on x86_64 there are up to 5 fields:
	 *  - bits 47:39 = PML4 index
	 *  - bits 38:30 = page-directory-pointer index
	 *  - bits 29:21 = page directory index
	 *  - bits 20:12 = page table index
	 *  - bits 11:0  = page offset
	 */
	unsigned short nfields;

	/** Sizes of individual address fields in bits.
	 * This array is sorted from lowest-level tables to the top level.
	 * The first element is the number of bits for the page offset
	 * (also known as page shift).
	 */
	unsigned short fieldsz[ADDRXLAT_FIELDS_MAX];
} addrxlat_paging_form_t;

/** Parameters of page table translation. */
typedef struct _addrxlat_param_pgt {
	addrxlat_fulladdr_t root;  /**< Root page table address. */

	/** Page table entry mask.
	 * Bits to be masked off the raw value. Zero means no masking.
	 */
	addrxlat_pte_t pte_mask;

	addrxlat_paging_form_t pf; /**< Paging form. */
} addrxlat_param_pgt_t;

/** Lookup table element.
 * This defines address mapping for a single object.
 * Addresses inside the object are mapped linearly using an offset.
 */
typedef struct _addrxlat_lookup_elem {
	addrxlat_addr_t orig;	/**< Original address. */
	addrxlat_addr_t dest;	/**< Corresponding destination address. */
} addrxlat_lookup_elem_t;

/** Parameters of table lookup translation. */
typedef struct _addrxlat_param_lookup {
	/** Max address offset inside each object.
	 * This is in fact object size - 1. However, specifying the end
	 * offset gives maximum flexibility (from 1-byte objects to the
	 * full size of the address space).
	 */
	addrxlat_addr_t endoff;

	/** Size of the table. */
	size_t nelem;

	/** Lookup table.
	 * The lookup table is owned by the translation method, i.e. it
	 * is freed when the method reference count becomes zero.
	 */
	addrxlat_lookup_elem_t *tbl;
} addrxlat_param_lookup_t;

/** Parameters of memory array translation. */
typedef struct _addrxlat_param_memarr {
	/** Base address of the translation array. */
	addrxlat_fulladdr_t base;

	/** Address bit shift.
	 * The address is shifted right by this many bits to
	 * get the corresponding index inside the memory array.
	 * The target value is then shifted left and remaining
	 * bits are copied from the source address.
	 * The intention is to allow indexing by page frame number.
	 */
	unsigned shift;

	/** Size of each array element. */
	unsigned elemsz;

	/** Size of the value. */
	unsigned valsz;
} addrxlat_param_memarr_t;

/** Parameters of the translation method. */
typedef union _addrxlat_param {
	addrxlat_param_custom_t custom; /**< For @ref ADDRXLAT_CUSTOM. */
	addrxlat_param_linear_t linear; /**< For @ref ADDRXLAT_LINEAR. */
	addrxlat_param_pgt_t pgt;	/**< For @ref ADDRXLAT_PGT. */
	addrxlat_param_lookup_t lookup; /**< For @ref ADDRXLAT_LOOKUP.  */
	addrxlat_param_memarr_t memarr; /**< For @ref ADDRXLAT_MEMARR. */
} addrxlat_param_t;

/** Address translation method. */
typedef struct _addrxlat_meth {
	/** Kind of translation method. */
	addrxlat_kind_t kind;

	/** Target address space. */
	addrxlat_addrspace_t target_as;

	/** Additional parameters. */
	addrxlat_param_t param;
} addrxlat_meth_t;

/** Translation system method index.
 *
 * A translation system uses a number of translation methods to do its job.
 * Any of them can be obtained with
 * @ref addrxlat_sys_get_meth or overridden with
 * @ref addrxlat_sys_set_meth using one of these indices.
 */
typedef enum _addrxlat_sys_meth {
	ADDRXLAT_SYS_METH_NONE = -1, /**< No method. */

	ADDRXLAT_SYS_METH_PGT,	   /**< Kernel-space page table. */
	ADDRXLAT_SYS_METH_UPGT,	   /**< User-space page table. */
	ADDRXLAT_SYS_METH_DIRECT,  /**< Direct mapping. */
	ADDRXLAT_SYS_METH_KTEXT,   /**< Kernel text mapping. */
	ADDRXLAT_SYS_METH_VMEMMAP, /**< Fixed VMEMMAP (on IBM POWER). */

	ADDRXLAT_SYS_METH_RDIRECT, /**< Reverse direct mapping. */

	/** Default machine physical to kernel physical mapping. */
	ADDRXLAT_SYS_METH_MACHPHYS_KPHYS,

	/** Default kernel physical to machine physical mapping. */
	ADDRXLAT_SYS_METH_KPHYS_MACHPHYS,

	/** First custom method. */
	ADDRXLAT_SYS_METH_CUSTOM

} addrxlat_sys_meth_t;

/** Number of custom methods. */
#define ADDRXLAT_SYS_METH_CUSTOM_NUM	8

/** Total number of system method indices. */
#define ADDRXLAT_SYS_METH_NUM \
	(ADDRXLAT_SYS_METH_CUSTOM + ADDRXLAT_SYS_METH_CUSTOM_NUM)

/** Definition of an address range.
 */
typedef struct _addrxlat_range {
	/** Max address offset inside the range. */
	addrxlat_addr_t endoff;

	/** Translation method index */
	addrxlat_sys_meth_t meth;
} addrxlat_range_t;

/**  Address translation map. */
typedef struct _addrxlat_map addrxlat_map_t;

/** Allocate and initialize a new address translation map.
 * @returns    New initialized map, or @c NULL on failure.
 *
 * This call can fail if and only if memory allocation fails.
 * The reference count of the newly created object is one.
 */
addrxlat_map_t *addrxlat_map_new(void);

/** Increment translation map reference counter.
 * @param map    Translation map.
 * @returns      New reference count.
 */
unsigned long addrxlat_map_incref(addrxlat_map_t *map);

/** Decrement translation map reference counter.
 * @param map    Translation map.
 * @returns      New reference count.
 *
 * If the new reference count is zero, the underlying object is freed
 * and its address must not be used afterwards.
 */
unsigned long addrxlat_map_decref(addrxlat_map_t *map);

/** Get translation map length.
 * @param map   Address translation map.
 * @returns     Number of elements in the map.
 */
size_t addrxlat_map_len(const addrxlat_map_t *map);

/** Get the ranges in a translation map.
 * @param map   Address translation map.
 * @returns     Poiner to the array of ranges.
 *
 * The first range in a map starts at address 0, and each following range
 * starts right after the previous one (i.e. at @c endoff + 1).
 */
const addrxlat_range_t *addrxlat_map_ranges(const addrxlat_map_t *map);

/** Set map translation for an address range.
 * @param map    Address translation map.
 * @param addr   Range start address.
 * @param range  Translation range definition.
 * @returns      Error status.
 *
 * If this function fails, the original @c map is left untouched.
 */
addrxlat_status
addrxlat_map_set(addrxlat_map_t *map, addrxlat_addr_t addr,
		 const addrxlat_range_t *range);

/** Find an address translation method in a translation map.
 * @param map   Address translation map.
 * @param addr  Address to be translated.
 * @returns     Translation method index.
 *
 * If no entry is found in the translation map, this function returns
 * @c ADDRXLAT_SYS_METH_NONE.
 */
addrxlat_sys_meth_t addrxlat_map_search(
	const addrxlat_map_t *map, addrxlat_addr_t addr);

/** Duplicate a translation map.
 * @param map  Source translation map.
 * @returns    Copy of @c map, or @c NULL on allocation failure.
 */
addrxlat_map_t *addrxlat_map_copy(const addrxlat_map_t *map);

/** Option number. */
typedef enum _addrxlat_optidx {
	/** Null option (ignored, use for padding).
	 * Type: none
	 */
	ADDRXLAT_OPT_NULL,

	/** Architecture name.
	 * Type: string
	 */
	ADDRXLAT_OPT_arch,

	/** Operating system type.
	 * Type: string
	 */
	ADDRXLAT_OPT_os_type,

	/** Operating system version.
	 * Type: number
	 */
	ADDRXLAT_OPT_version_code,

	/** Number of physical address bits.
	 * Type: number
	 */
	ADDRXLAT_OPT_phys_bits,

	/** Number of virtual address bits.
	 * Type: number
	 */
	ADDRXLAT_OPT_virt_bits,

	/** Page shift.
	 * Type: number
	 */
	ADDRXLAT_OPT_page_shift,

	/** Physical base address.
	 * Type: address
	 *
	 * Either the physical base address of an x86-64 Linux kernel,
	 * or the physical start address of an x86-64 Xen hypervisor.
	 */
	ADDRXLAT_OPT_phys_base,

	/** Root page table address.
	 * Type: full address
	 */
	ADDRXLAT_OPT_rootpgt,

	/** Xen p2m root machine frame number.
	 * Type: address
	 */
	ADDRXLAT_OPT_xen_p2m_mfn,

	/** Force the use of Xen m2p and p2m translation.
	 * Type: number
	 */
	ADDRXLAT_OPT_xen_xlat,

	/** Total number of addrxlat options. */
	ADDRXLAT_OPT_NUM
} addrxlat_optidx_t;

/** Single option value. */
typedef union _addrxlat_optval {
	/** String(-like) values. */
	const char *str;

	/** Number(-like) values. */
	unsigned long num;

	/** Simple address or offset. */
	addrxlat_addr_t addr;

	/** Full address (with address space).*/
	addrxlat_fulladdr_t fulladdr;
} addrxlat_optval_t;

/** Single option. */
typedef struct _addrxlat_opt {
	addrxlat_optidx_t idx;	/**< Option index. */
	addrxlat_optval_t val;	/**< Option value. */
} addrxlat_opt_t;

/** Helper to set @xref ADDRXLAT_OPT_arch in a type-safe manner. */
static inline void
addrxlat_opt_arch(addrxlat_opt_t *opt, const char *val)
{
	opt->idx = ADDRXLAT_OPT_arch;
	opt->val.str = val;
}

/** Helper to set @xref ADDRXLAT_OPT_os_type in a type-safe manner. */
static inline void
addrxlat_opt_os_type(addrxlat_opt_t *opt, const char *val)
{
	opt->idx = ADDRXLAT_OPT_os_type;
	opt->val.str = val;
}

/** Helper to set @xref ADDRXLAT_OPT_version_code in a type-safe manner. */
static inline void
addrxlat_opt_version_code(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_version_code;
	opt->val.num = val;
}

/** Helper to set @xref ADDRXLAT_OPT_phys_bits in a type-safe manner. */
static inline void
addrxlat_opt_phys_bits(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_phys_bits;
	opt->val.num = val;
}

/** Helper to set @xref ADDRXLAT_OPT_virt_bits in a type-safe manner. */
static inline void
addrxlat_opt_virt_bits(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_virt_bits;
	opt->val.num = val;
}

/** Helper to set @xref ADDRXLAT_OPT_page_shift in a type-safe manner. */
static inline void
addrxlat_opt_page_shift(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_page_shift;
	opt->val.num = val;
}

/** Helper to set @xref ADDRXLAT_OPT_phys_base in a type-safe manner. */
static inline void
addrxlat_opt_phys_base(addrxlat_opt_t *opt, addrxlat_addr_t val)
{
	opt->idx = ADDRXLAT_OPT_phys_base;
	opt->val.addr = val;
}

/** Helper to set @xref ADDRXLAT_OPT_rootpgt in a type-safe manner. */
static inline void
addrxlat_opt_rootpgt(addrxlat_opt_t *opt, const addrxlat_fulladdr_t *val)
{
	opt->idx = ADDRXLAT_OPT_rootpgt;
	opt->val.fulladdr = *val;
}

/** Helper to set @xref ADDRXLAT_OPT_xen_p2m_mfn in a type-safe manner. */
static inline void
addrxlat_opt_xen_p2m_mfn(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_xen_p2m_mfn;
	opt->val.num = val;
}

/** Helper to set @xref ADDRXLAT_OPT_xen_xlat in a type-safe manner. */
static inline void
addrxlat_opt_xen_xlat(addrxlat_opt_t *opt, unsigned long val)
{
	opt->idx = ADDRXLAT_OPT_xen_xlat;
	opt->val.num = val;
}

/** Linux kernel version code.
 * This macro can be used to convert a three-part Linux kernel version
 * to a single number for use as @xref ADDRXLAT_OPT_version_code.
 */
#define ADDRXLAT_VER_LINUX(a,b,c)	\
	(((a) << 16) + ((b) << 8) + (c))

/** Xen version code.
 * This macro can be used to convert a Xen major/minor version pair
 * to a single number for use as @xref ADDRXLAT_OPT_version_code.
 */
#define ADDRXLAT_VER_XEN(major,minor)	\
	(((major) << 16) | (minor))

/** Address translations system.
 * In addition to a @ref addrxlat_map_t, this structure also contains
 * any OS-specific data.
 */
typedef struct _addrxlat_sys addrxlat_sys_t;

/** Allocate a new translation system.
 * @returns  A new translation system, or @c NULL.
 *
 * This call can fail if and only if memory allocation fails.
 */
addrxlat_sys_t *addrxlat_sys_new(void);

/** Increment translation system reference counter.
 * @param sys    Translation system.
 * @returns      New reference count.
 */
unsigned long addrxlat_sys_incref(addrxlat_sys_t *sys);

/** Decrement translation system reference counter.
 * @param sys    Translation system.
 * @returns      New reference count.
 *
 * If the new reference count is zero, the underlying object is freed
 * and its address must not be used afterwards.
 */
unsigned long addrxlat_sys_decref(addrxlat_sys_t *sys);

/** Set up a translation system for a pre-defined operating system.
 * @param sys     Translation sytem.
 * @param ctx     Address translation context.
 * @param optc    Number of options.
 * @param opts    Additional options (ignored if @p optc is zero).
 * @returns       Error status.
 *
 * This function uses OS-specific data and built-in heuristics to
 * determine the translation map and page-table translation for an
 * operating system.
 */
addrxlat_status addrxlat_sys_os_init(
	addrxlat_sys_t *sys, addrxlat_ctx_t *ctx,
	unsigned optc, const addrxlat_opt_t *opts);

/** Translation system map index.
 *
 * The OS map object contains several translation maps to allow
 * translation between different address spaces. They can be
 * manipulated directly using
 * @ref addrxlat_sys_set_map and
 * @ref addrxlat_sys_get_map
 * using one of these indices.
 */
typedef enum _addrxlat_sys_map {
	/** Map virtual addresses using same method as hardware.
	 * Unlike @ref ADDRXLAT_SYS_MAP_KV_PHYS, translations
	 * in this map will always walk the hardware page table.
	 * This is mostly useful for implementing a verbose @c vtop
	 * command. It may be also used if translation using
	 * @ref ADDRXLAT_SYS_MAP_KV_PHYS results in the wrong
	 * physical address space and further translation is
	 * unavailable or too expensive.
	 * Note: This map may be incomplete (or not available)
	 * even if the virtual address can be in fact translated.
	 * For example, with IBM Power ISA with hashed page tables,
	 * only a fraction the address space is usually mapped.
	 */
	ADDRXLAT_SYS_MAP_HW,

	/** Map kernel virtual addresses to physical addresses.
	 * This translation accepts @ref ADDRXLAT_KVADDR on input
	 * and translates it to a physical address. This is either
	 * @ref ADDRXLAT_KPHYSADDR or @ref ADDRXLAT_MACHPHYSADDR,
	 * whichever is more efficient.
	 */
	ADDRXLAT_SYS_MAP_KV_PHYS,

	/** Map kernel physical addresses to a direct-mapped
	 * virtual address.
	 */
	ADDRXLAT_SYS_MAP_KPHYS_DIRECT,

	/** Map machine physical addresses to kernel physical addresses.
	 */
	ADDRXLAT_SYS_MAP_MACHPHYS_KPHYS,

	/** Map kernel physical addresses to machine physical addresses.
	 */
	ADDRXLAT_SYS_MAP_KPHYS_MACHPHYS,

	ADDRXLAT_SYS_MAP_NUM,	/**< Total number of indices. */
} addrxlat_sys_map_t;

/** Explicitly set the translation map of an OS map object.
 * @param sys     Translation system.
 * @param idx     Map index.
 * @param map     Translation map.
 */
void addrxlat_sys_set_map(
	addrxlat_sys_t *sys, addrxlat_sys_map_t idx,
	addrxlat_map_t *map);

/** Get the translation map of an OS map object.
 * @param sys     Translation system.
 * @param idx     Map index.
 * @returns       Associated translation map.
 */
addrxlat_map_t *addrxlat_sys_get_map(
	const addrxlat_sys_t *sys, addrxlat_sys_map_t idx);

/** Explicitly set an address translation method for a translation system.
 * @param sys     Translation system.
 * @param idx     Translation method index.
 * @param meth    New translation method.
 */
void addrxlat_sys_set_meth(
	addrxlat_sys_t *sys, addrxlat_sys_meth_t idx,
	const addrxlat_meth_t *meth);

/** Get a specific translation method of a translation system.
 * @param sys     Translation system.
 * @param idx     Translation method index.
 * @returns       Associated translation method.
 */
const addrxlat_meth_t *addrxlat_sys_get_meth(
	const addrxlat_sys_t *sys, addrxlat_sys_meth_t idx);

/** State of the current step in address translation. */
struct _addrxlat_step {
	/** Address translation context.
	 * The context is used for memory access and error reporting.
	 * This field must be initialized for @ref addrxlat_launch.
	 */
	addrxlat_ctx_t *ctx;

	/** Translation system.
	 * If not @c NULL, this system can be used to translate
	 * addresses for memory access.
	 * This field must be initialized for @ref addrxlat_launch
	 * (it may be @c NULL, but not an undefined value).
	 */
	addrxlat_sys_t *sys;

	/** Translation method for this particular translation.
	 * This field must be initialized for @ref addrxlat_launch.
	 */
	const addrxlat_meth_t *meth;

	/** Remaining steps.
	 * This field is set on a successful return from @ref addrxlat_launch.
	 * The value is then modified by @ref addrxlat_step. Translation is
	 * complete when it reaches zero.
	 * Note: Although this value is usually decremented by one in every
	 * translation step, it may be changed to any value in any step
	 * (e.g. on a huge page), so always check if the value has become
	 * zero after each call to @ref addrxlat_step.
	 */
	unsigned short remain;

	/** Size of the indexed element. The next level is found at:
	 *
	 *    @c base + @c idx [ @c remain - 1 ] * @c elemsz
	 *
	 * Note that @c base is updated using the above formula prior to
	 * calling the @c next_step function.
	 */
	unsigned elemsz;

	/** On input, base address of the page table.
	 * On output base address of the lower-level page table or
	 * the target physical address.
	 * This field is set on a successful return from @ref addrxlat_launch.
	 */
	addrxlat_fulladdr_t base;

	/** Raw translation step result.
	 * This field is set on a successful return from @ref addrxlat_step
	 * if it is meaningful for the translation method. It may be also used
	 * as input for the next translation step (but not for the first step).
	 */
	union {
		/** User-defined data for @ref ADDRXLAT_CUSTOM. */
		void *data;
		/** PTE value for @ref ADDRXLAT_PGT. */
		addrxlat_pte_t pte;
		/** Matched element for @ref ADDRXLAT_LOOKUP. */
		const addrxlat_lookup_elem_t *elem;
		/** Array content for @ref ADDRXLAT_MEMARR. */
		addrxlat_addr_t addr;
	} raw;

	/** Table indices at individual levels.
	 *
	 * There is one extra index, which contains the remaining part
	 * of the virtual address after all address fields were used.
	 * For example, on x86_64, only bits 47:0 are used for paging,
	 * the value of the bit field at 63:48 is put into the extra index.
	 */
	addrxlat_addr_t idx[ADDRXLAT_FIELDS_MAX + 1];
};

/** Make the first translation step (launch a translation).
 * @param step  (Initialized) translation step state.
 * @param addr  Address to be translated.
 * @returns     Error status.
 *
 * See @ref addrxlat_step_t for information on which fields in @step must
 * be initialized prior to calling this function.
 */
addrxlat_status addrxlat_launch(addrxlat_step_t *step, addrxlat_addr_t addr);

/** Perform one translation step.
 * @param step  Current step state.
 * @returns     Error status.
 */
addrxlat_status addrxlat_step(addrxlat_step_t *step);

/** Perform one complete address translation.
 * @param step  Initialized translation step state.
 * @returns     Error status.
 *
 * The source address to be translated is in @c step->base. All other
 * fields must be initialized as for a call to @ref addrxlat_launch.
 * On successful return, the resulting address is found in @c step->base.
 */
addrxlat_status addrxlat_walk(addrxlat_step_t *step);

/** Type of the @ref addrxlat_op callback.
 * @param data      Arbitrary user-supplied data.
 * @param[in] addr  Translated address.
 * @returns         Error status.
 */
typedef addrxlat_status addrxlat_op_fn(void *data,
				       const addrxlat_fulladdr_t *addr);

/** Callback function pointers and data. */
typedef struct _addrxlat_op_ctl {
	/** Address translation context. */
	addrxlat_ctx_t *ctx;

	/** Address translation system. */
	addrxlat_sys_t *sys;

	/** Operation callback. */
	addrxlat_op_fn *op;

	/** Arbitrary callback data, passed to callback functions. */
	void *data;

	/** Operation capabilities.
	 * This is a bit mask of address spaces that can be processed
	 * by the operation callback. Bits are enumerated by
	 * @ref addrxlat_addrspace_t (least significant bit first).
	 * @sa ADDRXLAT_CAPS
	 */
	unsigned long caps;
} addrxlat_op_ctl_t;

/** Perform a generic operation on a translated address.
 * @param ctl   Control structure.
 * @param addr  Address (in any address space).
 * @returns     Error status.
 *
 * NB: If there is no way to translate the source address space to
 * target address space, this function returns @ref ADDRXLAT_ERR_NOMETH.
 */
addrxlat_status addrxlat_op(const addrxlat_op_ctl_t *ctl,
			    const addrxlat_fulladdr_t *addr);

/** Translate a full address.
 * @param faddr  Full address to be translated.
 * @param as     Target address space.
 * @param ctx    Address translation context.
 * @param sys    Translation system.
 * @returns      Error status.
 *
 * Convert a full address to the target address space using the given
 * translation context and translation system.
 */
addrxlat_status addrxlat_fulladdr_conv(
	addrxlat_fulladdr_t *faddr, addrxlat_addrspace_t as,
	addrxlat_ctx_t *ctx, addrxlat_sys_t *sys);

#ifdef  __cplusplus
}
#endif

#endif	/* addrxlat.h */
