arm64: pKVM: Spawn protected guests
Signed-off-by: Will Deacon <willdeacon@google.com>
diff --git a/arm/aarch64/kvm-cpu.c b/arm/aarch64/kvm-cpu.c
index 9f3e858..352e4fc 100644
--- a/arm/aarch64/kvm-cpu.c
+++ b/arm/aarch64/kvm-cpu.c
@@ -120,6 +120,11 @@
if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0)
die_perror("KVM_SET_ONE_REG failed (x0)");
+ data = kvm->arch.kern_guest_start;
+ reg.id = ARM64_CORE_REG(regs.regs[4]);
+ if (ioctl(vcpu->vcpu_fd, KVM_SET_ONE_REG, ®) < 0)
+ die_perror("KVM_SET_ONE_REG failed (x4)");
+
/* pc = start of kernel image */
data = kvm->arch.kern_guest_start;
reg.id = ARM64_CORE_REG(regs.pc);
diff --git a/arm/aarch64/kvm.c b/arm/aarch64/kvm.c
index 56a0aed..2903d23 100644
--- a/arm/aarch64/kvm.c
+++ b/arm/aarch64/kvm.c
@@ -79,5 +79,47 @@
if (ipa_bits > max_ipa_bits)
die("Memory too large for this system (needs %d bits, %d available)", ipa_bits, max_ipa_bits);
- return KVM_VM_TYPE_ARM_IPA_SIZE(ipa_bits);
+ return KVM_VM_TYPE_ARM_IPA_SIZE(ipa_bits) | KVM_VM_TYPE_ARM_PROTECTED;
}
+
+static int protected_vm_init(struct kvm *kvm)
+{
+ struct kvm_enable_cap pvm_cap = {
+ .cap = KVM_CAP_ARM_PROTECTED_VM,
+ };
+ struct kvm_enable_cap hcall_cap = {
+ .cap = KVM_CAP_EXIT_HYPERCALL,
+ .args[0] = 1UL << ARM_SMCCC_KVM_FUNC_MEM_SHARE |
+ 1UL << ARM_SMCCC_KVM_FUNC_MEM_UNSHARE,
+ };
+ struct kvm_protected_vm_info pvm_info;
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ENABLE_CAP, &hcall_cap);
+ if (ret)
+ die("Failed to enable share/unshare hypercall trapping\n");
+
+ pvm_cap.flags = KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO;
+ pvm_cap.args[0] = (__u64)&pvm_info;
+ ret = ioctl(kvm->vm_fd, KVM_ENABLE_CAP, &pvm_cap);
+ if (ret)
+ die("Failed to query pvm info\n");
+
+ if (pvm_info.firmware_size) {
+ u64 end = kvm->arch.initrd_guest_start;
+ u64 start = end - pvm_info.firmware_size;
+
+ pr_info("Placing pvmfw at 0x%llx - 0x%llx", start, end);
+ pvm_cap.flags = KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE;
+ pvm_cap.args[0] = start;
+ ret = ioctl(kvm->vm_fd, KVM_ENABLE_CAP, &pvm_cap);
+ if (ret)
+ die("Failed to enable VM protection\n");
+ } else {
+ pr_warning("No pvmfw found; booting protected guest directly");
+ }
+
+ pr_info("VM protection enabled");
+ return ret;
+}
+dev_init(protected_vm_init);
diff --git a/arm/fdt.c b/arm/fdt.c
index 635de7f..0cd45d7 100644
--- a/arm/fdt.c
+++ b/arm/fdt.c
@@ -116,6 +116,7 @@
void (*)(void *, u8, enum irq_type));
void (*generate_cpu_peripheral_fdt_nodes)(void *, struct kvm *)
= kvm->cpus[0]->generate_fdt_nodes;
+ u64 resv_mem_prop;
/* Create new tree without a reserve map */
_FDT(fdt_create(fdt, FDT_MAX_SIZE));
@@ -163,6 +164,21 @@
_FDT(fdt_property(fdt, "reg", mem_reg_prop, sizeof(mem_reg_prop)));
_FDT(fdt_end_node(fdt));
+ /* Reserved memory (restricted DMA) */
+ _FDT(fdt_begin_node(fdt, "reserved-memory"));
+ _FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
+ _FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
+ _FDT(fdt_property(fdt, "ranges", NULL, 0));
+
+ _FDT(fdt_begin_node(fdt, "restricted_dma_reserved"));
+ _FDT(fdt_property_string(fdt, "compatible", "restricted-dma-pool"));
+ resv_mem_prop = cpu_to_fdt64(SZ_2M);
+ _FDT(fdt_property(fdt, "size", &resv_mem_prop, sizeof(resv_mem_prop)));
+ _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_DMA));
+ _FDT(fdt_end_node(fdt));
+
+ _FDT(fdt_end_node(fdt));
+
/* CPU and peripherals (interrupt controller, timers, etc) */
generate_cpu_nodes(fdt, kvm);
if (generate_cpu_peripheral_fdt_nodes)
diff --git a/arm/include/arm-common/fdt-arch.h b/arm/include/arm-common/fdt-arch.h
index 60c2d40..81df744 100644
--- a/arm/include/arm-common/fdt-arch.h
+++ b/arm/include/arm-common/fdt-arch.h
@@ -1,6 +1,6 @@
#ifndef ARM__FDT_H
#define ARM__FDT_H
-enum phandles {PHANDLE_RESERVED = 0, PHANDLE_GIC, PHANDLE_MSI, PHANDLES_MAX};
+enum phandles {PHANDLE_RESERVED = 0, PHANDLE_GIC, PHANDLE_MSI, PHANDLE_DMA, PHANDLES_MAX};
#endif /* ARM__FDT_H */
diff --git a/arm/kvm.c b/arm/kvm.c
index 5aea18f..f8ed9e9 100644
--- a/arm/kvm.c
+++ b/arm/kvm.c
@@ -163,6 +163,7 @@
kvm->arch.initrd_guest_start,
kvm->arch.initrd_size);
} else {
+ kvm->arch.initrd_guest_start = kvm->arch.dtb_guest_start;
kvm->arch.initrd_size = 0;
}
diff --git a/arm/pci.c b/arm/pci.c
index 2251f62..c533e98 100644
--- a/arm/pci.c
+++ b/arm/pci.c
@@ -69,6 +69,7 @@
_FDT(fdt_property(fdt, "reg", &cfg_reg_prop, sizeof(cfg_reg_prop)));
_FDT(fdt_property(fdt, "ranges", ranges, sizeof(ranges)));
_FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_MSI));
+ _FDT(fdt_property_cell(fdt, "memory-region", PHANDLE_DMA));
/* Generate the interrupt map ... */
dev_hdr = device__first_dev(DEVICE_BUS_PCI);
diff --git a/builtin-run.c b/builtin-run.c
index 9a1a0c1..866ae9d 100644
--- a/builtin-run.c
+++ b/builtin-run.c
@@ -524,6 +524,7 @@
static char default_name[20];
unsigned int nr_online_cpus;
struct kvm *kvm = kvm__new();
+ int ret;
if (IS_ERR(kvm))
return kvm;
@@ -685,6 +686,9 @@
kvm->cfg.nrcpus, kvm->cfg.guest_name);
}
+ ret = mprotect(kvm->ram_start, kvm->ram_size, PROT_NONE);
+ pr_info("mprotect() guest memory as PROT_NONE: (%d)", ret);
+
if (init_list__init(kvm) < 0)
die ("Initialisation failed");
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 5e3f12d..7023a62 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -997,6 +997,23 @@
#define KVM_CAP_ARM_PTRAUTH_GENERIC 172
#define KVM_CAP_PMU_EVENT_FILTER 173
+/* XXX: -------- pKVM -------- */
+#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 8)
+
+#define KVM_CAP_EXIT_HYPERCALL 201
+#define ARM_SMCCC_KVM_FUNC_MEM_SHARE 3
+#define ARM_SMCCC_KVM_FUNC_MEM_UNSHARE 4
+
+#define KVM_CAP_ARM_PROTECTED_VM 207
+#define KVM_CAP_ARM_PROTECTED_VM_FLAGS_ENABLE 0
+#define KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO 1
+/* ----------------------------*/
+
+struct kvm_protected_vm_info {
+ __u64 firmware_size;
+ __u64 reserved[7];
+};
+
#ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing_irqchip {
diff --git a/kvm-cpu.c b/kvm-cpu.c
index 7dec088..cd0649b 100644
--- a/kvm-cpu.c
+++ b/kvm-cpu.c
@@ -238,6 +238,26 @@
goto exit_kvm;
};
break;
+ case KVM_EXIT_HYPERCALL: {
+ u64 hcall = cpu->kvm_run->hypercall.nr;
+
+ if (hcall == ARM_SMCCC_KVM_FUNC_MEM_SHARE ||
+ hcall == ARM_SMCCC_KVM_FUNC_MEM_UNSHARE) {
+ u64 ipa = cpu->kvm_run->hypercall.args[0];
+ int ret, prot;
+ void *addr;
+
+ addr = guest_flat_to_host(cpu->kvm, ipa);
+ prot = hcall == ARM_SMCCC_KVM_FUNC_MEM_SHARE ?
+ PROT_READ | PROT_WRITE : PROT_NONE;
+
+ ret = mprotect(addr, PAGE_SIZE, prot);
+ pr_info("mprotect(%p, 0x%lx, 0x%x): %d", addr,
+ PAGE_SIZE, prot, ret);
+ }
+
+ break;
+ }
default: {
bool ret;