@@ -2930,7 +2930,7 @@ static void kvm_eat_signals(CPUState *cpu)
} while (sigismember(&chkset, SIG_IPI));
}
-static int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
+int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
{
MemoryRegionSection section;
ram_addr_t offset;
@@ -550,4 +550,6 @@ int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp);
int kvm_set_memory_attributes_private(hwaddr start, hwaddr size);
int kvm_set_memory_attributes_shared(hwaddr start, hwaddr size);
+
+int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private);
#endif
@@ -898,6 +898,58 @@ static hwaddr tdx_shared_bit(X86CPU *cpu)
return (cpu->phys_bits > 48) ? BIT_ULL(51) : BIT_ULL(47);
}
+/* 64MB at most in one call. What value is appropriate? */
+#define TDX_MAP_GPA_MAX_LEN (64 * 1024 * 1024)
+
+static int tdx_handle_map_gpa(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
+{
+ hwaddr shared_bit = tdx_shared_bit(cpu);
+ hwaddr gpa = vmcall->in_r12 & ~shared_bit;
+ bool private = !(vmcall->in_r12 & shared_bit);
+ hwaddr size = vmcall->in_r13;
+ bool retry = false;
+ int ret = 0;
+
+ vmcall->status_code = TDG_VP_VMCALL_INVALID_OPERAND;
+
+ if (!QEMU_IS_ALIGNED(gpa, 4096) || !QEMU_IS_ALIGNED(size, 4096)) {
+ vmcall->status_code = TDG_VP_VMCALL_ALIGN_ERROR;
+ return 0;
+ }
+
+ /* Overflow case. */
+ if (gpa + size < gpa) {
+ return 0;
+ }
+ if (gpa >= (1ULL << cpu->phys_bits) ||
+ gpa + size >= (1ULL << cpu->phys_bits)) {
+ return 0;
+ }
+
+ if (size > TDX_MAP_GPA_MAX_LEN) {
+ retry = true;
+ size = TDX_MAP_GPA_MAX_LEN;
+ }
+
+ if (size > 0) {
+ ret = kvm_convert_memory(gpa, size, private);
+ }
+
+ if (!ret) {
+ if (retry) {
+ vmcall->status_code = TDG_VP_VMCALL_RETRY;
+ vmcall->out_r11 = gpa + size;
+ if (!private) {
+ vmcall->out_r11 |= shared_bit;
+ }
+ } else {
+ vmcall->status_code = TDG_VP_VMCALL_SUCCESS;
+ }
+ }
+
+ return 0;
+}
+
static void tdx_get_quote_completion(struct tdx_generate_quote_task *task)
{
int ret;
@@ -1070,6 +1122,8 @@ static int tdx_handle_vmcall(X86CPU *cpu, struct kvm_tdx_vmcall *vmcall)
}
switch (vmcall->subfunction) {
+ case TDG_VP_VMCALL_MAP_GPA:
+ return tdx_handle_map_gpa(cpu, vmcall);
case TDG_VP_VMCALL_GET_QUOTE:
return tdx_handle_get_quote(cpu, vmcall);
case TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT:
@@ -18,6 +18,7 @@ typedef struct TdxGuestClass {
ConfidentialGuestSupportClass parent_class;
} TdxGuestClass;
+#define TDG_VP_VMCALL_MAP_GPA 0x10001ULL
#define TDG_VP_VMCALL_GET_QUOTE 0x10002ULL
#define TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004ULL