1#include <asm/tdx.h>
2#include <asm/pgtable.h>
3
4static unsigned long try_accept_one(phys_addr_t start, unsigned long len,
5				    enum pg_level pg_level)
6{
7	unsigned long accept_size = page_level_size(pg_level);
8	struct tdx_module_args args = {};
9	u8 page_size;
10
11	if (!IS_ALIGNED(start, accept_size))
12		return 0;
13
14	if (len < accept_size)
15		return 0;
16
17	/*
18	 * Pass the page physical address to the TDX module to accept the
19	 * pending, private page.
20	 *
21	 * Bits 2:0 of RCX encode page size: 0 - 4K, 1 - 2M, 2 - 1G.
22	 */
23	switch (pg_level) {
24	case PG_LEVEL_4K:
25		page_size = TDX_PS_4K;
26		break;
27	case PG_LEVEL_2M:
28		page_size = TDX_PS_2M;
29		break;
30	case PG_LEVEL_1G:
31		page_size = TDX_PS_1G;
32		break;
33	default:
34		return 0;
35	}
36
37	args.rcx = start | page_size;
38	if (__tdcall(TDG_MEM_PAGE_ACCEPT, &args))
39		return 0;
40
41	return accept_size;
42}
43
44bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
45{
46	/*
47	 * For shared->private conversion, accept the page using
48	 * TDG_MEM_PAGE_ACCEPT TDX module call.
49	 */
50	while (start < end) {
51		unsigned long len = end - start;
52		unsigned long accept_size;
53
54		/*
55		 * Try larger accepts first. It gives chance to VMM to keep
56		 * 1G/2M Secure EPT entries where possible and speeds up
57		 * process by cutting number of hypercalls (if successful).
58		 */
59
60		accept_size = try_accept_one(start, len, PG_LEVEL_1G);
61		if (!accept_size)
62			accept_size = try_accept_one(start, len, PG_LEVEL_2M);
63		if (!accept_size)
64			accept_size = try_accept_one(start, len, PG_LEVEL_4K);
65		if (!accept_size)
66			return false;
67		start += accept_size;
68	}
69
70	return true;
71}
72
73noinstr u64 __tdx_hypercall(struct tdx_module_args *args)
74{
75	/*
76	 * For TDVMCALL explicitly set RCX to the bitmap of shared registers.
77	 * The caller isn't expected to set @args->rcx anyway.
78	 */
79	args->rcx = TDVMCALL_EXPOSE_REGS_MASK;
80
81	/*
82	 * Failure of __tdcall_saved_ret() indicates a failure of the TDVMCALL
83	 * mechanism itself and that something has gone horribly wrong with
84	 * the TDX module.  __tdx_hypercall_failed() never returns.
85	 */
86	if (__tdcall_saved_ret(TDG_VP_VMCALL, args))
87		__tdx_hypercall_failed();
88
89	/* TDVMCALL leaf return code is in R10 */
90	return args->r10;
91}
92