acrn-hypervisor/hypervisor/arch/x86/trusty2.c

224 lines
6.1 KiB
C

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hypervisor.h>
#ifdef WORKAROUND_FOR_TRUSTY_4G_MEM
uint64_t gpa2hpa_for_trusty(struct vm *vm, uint64_t gpa)
{
uint64_t hpa = 0UL;
struct entry_params entry;
struct map_params map_params;
map_params.page_table_type = PTT_EPT;
map_params.pml4_base = HPA2HVA(vm->arch_vm.sworld_eptp);
map_params.pml4_inverted = HPA2HVA(vm->arch_vm.m2p);
obtain_last_page_table_entry(&map_params, &entry, (void *)gpa, true);
if (entry.entry_present == PT_PRESENT) {
hpa = ((entry.entry_val & (~(entry.page_size - 1UL)))
| (gpa & (entry.page_size - 1UL)));
pr_dbg("GPA2HPA: 0x%llx->0x%llx", gpa, hpa);
} else {
pr_err("VM %d GPA2HPA: failed for gpa 0x%llx",
vm->attr.boot_idx, gpa);
}
return hpa;
}
/**
* @defgroup trusty_apis Trusty APIs
*
* This is a special group that includes all APIs
* related to Trusty
*
* @{
*/
/**
* @brief Create Secure World EPT hierarchy
*
* Create Secure World EPT hierarchy, construct new PML4/PDPT,
* reuse PD/PT parse from vm->arch_vm->ept
*
* @param vm pointer to a VM with 2 Worlds
* @param gpa_orig original gpa allocated from vSBL
* @param size LK size (16M by default)
* @param gpa_rebased gpa rebased to offset xxx (511G_OFFSET)
*
*/
void create_secure_world_ept(struct vm *vm, uint64_t gpa_orig,
int64_t size, uint64_t gpa_rebased)
{
uint64_t nworld_pml4e = 0UL;
uint64_t sworld_pml4e = 0UL;
struct entry_params entry;
struct map_params map_params;
uint64_t gpa_uos = gpa_orig;
uint64_t gpa_sos;
uint64_t adjust_size;
uint64_t mod;
uint64_t hpa = gpa2hpa(vm, gpa_uos);
void *sub_table_addr = NULL, *pml4_base = NULL;
int i;
struct vm *vm0 = get_vm_from_vmid(0);
struct vcpu *vcpu;
if (vm0 == NULL) {
pr_err("Parse vm0 context failed.");
return;
}
if (!vm->sworld_control.sworld_enabled
|| vm->arch_vm.sworld_eptp != 0UL) {
pr_err("Sworld is not enabled or Sworld eptp is not NULL");
return;
}
/* Backup secure world info, will be used when
* destroy secure world
*/
vm->sworld_control.sworld_memory.base_gpa = gpa_rebased;
vm->sworld_control.sworld_memory.base_hpa = hpa;
vm->sworld_control.sworld_memory.length = (uint64_t)size;
vm->sworld_control.sworld_memory.gpa_sos = hpa2gpa(vm0, hpa);
vm->sworld_control.sworld_memory.gpa_uos = gpa_orig;
/* Copy PDPT entries from Normal world to Secure world
* Secure world can access Normal World's memory,
* but Normal World can not access Secure World's memory.
* The PML4/PDPT for Secure world are separated from
* Normal World.PD/PT are shared in both Secure world's EPT
* and Normal World's EPT
*/
pml4_base = alloc_paging_struct();
vm->arch_vm.sworld_eptp = HVA2HPA(pml4_base);
/* The trusty memory is remapped to guest physical address
* of gpa_rebased to gpa_rebased + size
*/
sub_table_addr = alloc_paging_struct();
sworld_pml4e = HVA2HPA(sub_table_addr) | IA32E_EPT_R_BIT |
IA32E_EPT_W_BIT |
IA32E_EPT_X_BIT;
mem_write64(pml4_base, sworld_pml4e);
nworld_pml4e = mem_read64(HPA2HVA(vm->arch_vm.nworld_eptp));
(void)memcpy_s(HPA2HVA(sworld_pml4e & IA32E_REF_MASK), CPU_PAGE_SIZE,
HPA2HVA(nworld_pml4e & IA32E_REF_MASK), CPU_PAGE_SIZE);
/* Unmap gpa_orig~gpa_orig+size from guest normal world ept mapping */
map_params.page_table_type = PTT_EPT;
while (size > 0) {
map_params.pml4_base = HPA2HVA(vm->arch_vm.nworld_eptp);
map_params.pml4_inverted = HPA2HVA(vm->arch_vm.m2p);
obtain_last_page_table_entry(&map_params, &entry,
(void *)gpa_uos, true);
mod = (gpa_uos % entry.page_size);
adjust_size = (mod) ? (entry.page_size - mod) : entry.page_size;
if ((uint64_t)size < entry.page_size)
adjust_size = size;
hpa = gpa2hpa(vm, gpa_uos);
/* Unmap from normal world */
unmap_mem(&map_params, (void *)hpa,
(void *)gpa_uos, adjust_size, 0U);
/* Map to secure world */
map_params.pml4_base = HPA2HVA(vm->arch_vm.sworld_eptp);
map_mem(&map_params, (void *)hpa,
(void *)gpa_rebased, adjust_size,
(IA32E_EPT_R_BIT |
IA32E_EPT_W_BIT |
IA32E_EPT_X_BIT |
IA32E_EPT_WB));
/* Unmap trusty memory space from sos ept mapping*/
map_params.pml4_base = HPA2HVA(vm0->arch_vm.nworld_eptp);
map_params.pml4_inverted = HPA2HVA(vm0->arch_vm.m2p);
/* Get the gpa address in SOS */
gpa_sos = hpa2gpa(vm0, hpa);
unmap_mem(&map_params, (void *)hpa,
(void *)gpa_sos, adjust_size, 0U);
gpa_uos += adjust_size;
size -= adjust_size;
gpa_rebased += adjust_size;
}
foreach_vcpu(i, vm, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH);
}
foreach_vcpu(i, vm0, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH);
}
}
void destroy_secure_world(struct vm *vm)
{
struct map_params map_params;
struct entry_params entry;
struct vm *vm0 = get_vm_from_vmid(0);
uint64_t hpa;
uint64_t mod;
int i;
struct vcpu *vcpu;
uint64_t adjust_size;
uint64_t gpa = vm->sworld_control.sworld_memory.base_gpa;
int64_t size = (int64_t)vm->sworld_control.sworld_memory.length;
if (vm0 == NULL) {
pr_err("Parse vm0 context failed.");
return;
}
map_params.page_table_type = PTT_EPT;
while (size > 0) {
/* clear trusty memory space */
map_params.pml4_base = HPA2HVA(vm->arch_vm.sworld_eptp);
map_params.pml4_inverted = HPA2HVA(vm->arch_vm.m2p);
obtain_last_page_table_entry(&map_params, &entry,
(void *)gpa, true);
hpa = gpa2hpa_for_trusty(vm, gpa);
mod = (hpa % entry.page_size);
adjust_size = (mod) ? (entry.page_size - mod) : entry.page_size;
if ((uint64_t)size < entry.page_size)
adjust_size = size;
(void)memset(HPA2HVA(hpa), 0, adjust_size);
/* restore memory to SOS ept mapping */
map_params.pml4_base = HPA2HVA(vm0->arch_vm.nworld_eptp);
map_params.pml4_inverted = HPA2HVA(vm0->arch_vm.m2p);
/* here gpa=hpa because sos 1:1 mapping
* this is a temp solution
*/
map_mem(&map_params, (void *)hpa,
(void *)hpa,
adjust_size,
(IA32E_EPT_R_BIT |
IA32E_EPT_W_BIT |
IA32E_EPT_X_BIT |
IA32E_EPT_WB));
size -= adjust_size;
gpa += adjust_size;
}
foreach_vcpu(i, vm, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH);
}
foreach_vcpu(i, vm0, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH);
}
}
#endif