diff --git a/hypervisor/arch/x86/guest/vmcall.c b/hypervisor/arch/x86/guest/vmcall.c index a7db2611e..4e80a37ed 100644 --- a/hypervisor/arch/x86/guest/vmcall.c +++ b/hypervisor/arch/x86/guest/vmcall.c @@ -116,6 +116,11 @@ int vmcall_vmexit_handler(struct vcpu *vcpu) ret = hcall_set_vm_memory_regions(vm, param1); break; + case HC_VM_WRITE_PROTECT_PAGE: + ret = hcall_write_protect_page(vm, (uint16_t)param1, param2); + break; + + case HC_VM_PCI_MSIX_REMAP: /* param1: vmid */ ret = hcall_remap_pci_msix(vm, (uint16_t)param1, param2); diff --git a/hypervisor/common/hypercall.c b/hypervisor/common/hypercall.c index e4dcc0dda..da1fa5932 100644 --- a/hypervisor/common/hypercall.c +++ b/hypervisor/common/hypercall.c @@ -558,6 +558,60 @@ int32_t hcall_set_vm_memory_regions(struct vm *vm, uint64_t param) return 0; } +static int32_t write_protect_page(struct vm *vm, struct wp_data *wp) +{ + uint64_t hpa, base_paddr; + uint64_t prot_set; + uint64_t prot_clr; + + hpa = gpa2hpa(vm, wp->gpa); + dev_dbg(ACRN_DBG_HYCALL, "[vm%d] gpa=0x%x hpa=0x%x", + vm->attr.id, wp->gpa, hpa); + + base_paddr = get_hv_image_base(); + if (((hpa <= base_paddr) && (hpa + CPU_PAGE_SIZE > base_paddr)) || + ((hpa >= base_paddr) && + (hpa < base_paddr + CONFIG_RAM_SIZE))) { + pr_err("%s: overlap the HV memory region.", __func__); + return -EINVAL; + } + + prot_set = (wp->set != 0U) ? 0UL : EPT_WR; + prot_clr = (wp->set != 0U) ? EPT_WR : 0UL; + + return ept_mr_modify(vm, (uint64_t *)vm->arch_vm.nworld_eptp, + wp->gpa, CPU_PAGE_SIZE, prot_set, prot_clr); +} + +int32_t hcall_write_protect_page(struct vm *vm, uint16_t vmid, uint64_t wp_gpa) +{ + struct wp_data wp; + struct vm *target_vm = get_vm_from_vmid(vmid); + + if ((vm == NULL) || (target_vm == NULL)) { + return -EINVAL; + } + + if (!is_vm0(vm)) { + pr_err("%s: Not coming from service vm", __func__); + return -EPERM; + } + + if (is_vm0(target_vm)) { + pr_err("%s: Targeting to service vm", __func__); + return -EINVAL; + } + + (void)memset((void *)&wp, 0U, sizeof(wp)); + + if (copy_from_gpa(vm, &wp, wp_gpa, sizeof(wp)) != 0) { + pr_err("%s: Unable copy param to vm\n", __func__); + return -EFAULT; + } + + return write_protect_page(target_vm, &wp); +} + int32_t hcall_remap_pci_msix(struct vm *vm, uint16_t vmid, uint64_t param) { int32_t ret = 0; diff --git a/hypervisor/include/common/hypercall.h b/hypervisor/include/common/hypercall.h index 42bac48a2..3a5e9a512 100644 --- a/hypervisor/include/common/hypercall.h +++ b/hypervisor/include/common/hypercall.h @@ -228,6 +228,18 @@ int32_t hcall_set_vm_memory_region(struct vm *vm, uint16_t vmid, uint64_t param) */ int32_t hcall_set_vm_memory_regions(struct vm *vm, uint64_t param); +/** + * @brief change guest memory page write permission + * + * @param vm Pointer to VM data structure + * @param vmid ID of the VM + * @param param guest physical address. This gpa points to + * struct wp_data + * + * @return 0 on success, non-zero on error. + */ +int32_t hcall_write_protect_page(struct vm *vm, uint16_t vmid, uint64_t param); + /** * @brief remap PCI MSI interrupt * diff --git a/hypervisor/include/public/acrn_hv_defs.h b/hypervisor/include/public/acrn_hv_defs.h index 30eca2a26..117984432 100644 --- a/hypervisor/include/public/acrn_hv_defs.h +++ b/hypervisor/include/public/acrn_hv_defs.h @@ -53,6 +53,7 @@ #define HC_VM_SET_MEMORY_REGION _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00UL) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01UL) #define HC_VM_SET_MEMORY_REGIONS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02UL) +#define HC_VM_WRITE_PROTECT_PAGE _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x03UL) /* PCI assignment*/ #define HC_ID_PCI_BASE 0x50UL @@ -109,6 +110,7 @@ struct vm_memory_region { #define MR_ADD 0U #define MR_DEL 2U +#define MR_MODIFY 3U /** set memory region type: MR_ADD or MAP_DEL */ uint32_t type; @@ -148,6 +150,24 @@ struct set_regions { uint64_t regions_gpa; } __attribute__((aligned(8))); +/** + * @brief Info to change guest one page write protect permission + * + * the parameter for HC_VM_WRITE_PROTECT_PAGE hypercall + */ +struct wp_data { + /** set page write protect permission. + * ture: set the wp; flase: clear the wp + */ + uint8_t set; + + /** Reserved */ + uint64_t pad:56; + + /** the guest physical address of the page to change */ + uint64_t gpa; +} __aligned(8); + /** * Setup parameter for share buffer, used for HC_SETUP_SBUF hypercall */