// SPDX-License-Identifier: GPL-2.0


#ifndef __LINUX_ORBVM_H
#define __LINUX_ORBVM_H

#include <linux/orbvm_core.h>
#include <linux/build_bug.h>
#include <linux/pgtable.h>
#include <linux/arm-smccc.h>



#define ORBVM_HVC_ID(id) ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_VENDOR_HYP, 0xe000 + id)

#define ORBVM_FEATURES ORBVM_HVC_ID(1)
#define ORBVM_WFK ORBVM_HVC_ID(2)
#define ORBVM_KICK ORBVM_HVC_ID(3)
#define ORBVM_IOR ORBVM_HVC_ID(4)
#define ORBVM_SET_PVG ORBVM_HVC_ID(5)
#define ORBVM_SET_REG ORBVM_HVC_ID(6)
#define ORBVM_MMIO_WRITE32 ORBVM_HVC_ID(7)

#define ORBVM_FEAT_FS (1 << 0)
#define ORBVM_FEAT_BLK (1 << 1)
#define ORBVM_FEAT_MMIO (1 << 2)
#define ORBVM_FEAT_CONSOLE (1 << 3)

#define HVC_DEVICE_VIRTIOFS_ROOT 0
#define HVC_DEVICE_VIRTIOFS_ROSETTA 1
#define HVC_DEVICE_BLOCK_START 2000
#define HVC_DEVICE_CONSOLE_START 3000



#define FPR_TYPE_FREE 0
#define FPR_TYPE_UNREPORT 1

#define FPR_CAPACITY 4096
#define FPR_FLUSH_THRESHOLD 1

#define UNREPORT_FLUSH_THRESHOLD_BYTES (256ULL * 1024 * 1024) // MiB
#define UNREPORT_FLUSH_THRESHOLD(order, array) (min(ARRAY_SIZE(array), (UNREPORT_FLUSH_THRESHOLD_BYTES / PAGE_SIZE) >> order))

struct orbvm_fpr_request {
	u32 type;
	u32 guest_page_size;

	// only for HVC
	u64 descs_addr;
	u32 nr_descs;
};

#define PRDESC_PHYS_MASK GENMASK_ULL(51, 0)
#define PRDESC_ORDER_MASK GENMASK_ULL(62, 52)
#define PRDESC_PRESENT_MASK GENMASK_ULL(63, 63)

static inline struct page *prdesc_page(prdesc_t desc)
{
	return pfn_to_page(__phys_to_pfn(desc & PRDESC_PHYS_MASK));
}

static inline unsigned int prdesc_order(prdesc_t desc)
{
	return (desc & PRDESC_ORDER_MASK) >> 52;
}

static inline bool prdesc_present(prdesc_t desc)
{
	return desc & PRDESC_PRESENT_MASK;
}

static inline void prdesc_clear_present(prdesc_t *desc)
{
	*desc &= ~PRDESC_PRESENT_MASK;
}

// must be a macro because vmemmap
static inline prdesc_t prdesc_from_page(struct page *page, unsigned int order)
{
	return (prdesc_t)__pfn_to_phys(page_to_pfn(page)) | ((u64)order << 52) | PRDESC_PRESENT_MASK;
}



typedef u64 fsdesc_t;

#define FSDESC_PHYS_MASK GENMASK_ULL(47, 0)
#define FSDESC_LEN_MASK GENMASK_ULL(63, 48)

#ifdef CONFIG_ARM64
static_assert(CONFIG_ARM64_PA_BITS <= 48);
#endif

static inline fsdesc_t fsdesc_from_phys(phys_addr_t phys, u16 len)
{
	return (fsdesc_t)phys | ((u64)len << 48);
}

struct orbvm_fuse_arg {
	phys_addr_t addr;
	u64 size;
} __attribute__((packed));

struct orbvm_args {
	u32 in_numargs;
	u32 out_numargs;
	u32 in_numpages;
	u32 out_numpages;
	struct orbvm_fuse_arg in_args[4];
	struct orbvm_fuse_arg out_args[3];

	// pages
	fsdesc_t pages[128];
};



#define ORBVM_BLK_MAX_SEGS 224

typedef u64 blkdesc_t;

#define BLKDESC_PHYS_MASK GENMASK_ULL(47, 0)
#define BLKDESC_LEN_SECTORS_MASK GENMASK_ULL(63, 48)

static inline blkdesc_t blkdesc_from_phys(phys_addr_t phys, u16 len_sectors)
{
	return (blkdesc_t)phys | ((u64)len_sectors << 48);
}

static inline blkdesc_t blkdesc_from_off(u64 pos_sectors, u16 len_sectors)
{
	return (blkdesc_t)pos_sectors | ((u64)len_sectors << 48);
}

struct orbvm_blk_req_hdr {
	u16 type;
	u16 flags;
	u16 nr_segs;
	u64 discard_len;
	u64 start_off;
};

struct orbvm_blk_req {
	struct orbvm_blk_req_hdr hdr;
	blkdesc_t segs[ORBVM_BLK_MAX_SEGS];
};

enum {
	ORBVM_BLK_FLAG_PREFLUSH = 1 << 0,
	ORBVM_BLK_FLAG_POSTFLUSH = 1 << 1,
};



#define ORBVM_CONSOLE_REQ_WRITE 0

struct orbvm_console_req {
	u16 type;
	phys_addr_t addr;
	u64 len;
};



#ifdef CONFIG_ARM64
static inline int orbvm_call2(u32 id, u64 arg1, u64 arg2)
{
	struct arm_smccc_res res;
	arm_smccc_1_1_invoke(id, arg1, arg2, &res);
	return res.a0;
}

static inline int orbvm_call0(u32 id)
{
	struct arm_smccc_res res;
	arm_smccc_1_1_invoke(id, &res);
	return res.a0;
}

static inline int orbvm_call1(u32 id, u64 arg1)
{
	struct arm_smccc_res res;
	arm_smccc_1_1_invoke(id, arg1, &res);
	return res.a0;
}
#endif

#ifdef CONFIG_X86_64
#include <linux/kvm_para.h>

static inline int orbvm_call1(u32 id, u64 arg1)
{
	return kvm_hypercall1(id, arg1);
}

static inline int orbvm_call2(u32 id, u64 arg1, u64 arg2)
{
	return kvm_hypercall2(id, arg1, arg2);
}
#endif

static inline bool orbvm_has_feature(u64 feature)
{
	return orbvm_call1(ORBVM_FEATURES, feature) == feature;
}

#endif 
