hv: pio_region: add hypercalls to support pio device pass through

Add two hypercalls HC_ASSIGN_PIO_REGION and HC_DEASSIGN_PIO_REGION to
support PIO device pass through for post-launched VM.

Add has_direct_pio_access function to check the pio access state of VM
before allow/deny guest pio access.

Align querying, setting and clearing interfaces of VM io.bitmap with
general bitmap operation function.

Tracked-On: #8635
Signed-off-by: Yichong Tang <yichong.tang@intel.com>
Reviewed-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
yichongt 2024-06-28 10:51:50 +08:00 committed by wenlingz
parent fb2acbacb1
commit b138dd1eaf
7 changed files with 161 additions and 2 deletions

View File

@ -82,6 +82,10 @@ static const struct hc_dispatch hc_dispatch_table[] = {
.handler = hcall_add_vdev}, .handler = hcall_add_vdev},
[HC_IDX(HC_REMOVE_VDEV)] = { [HC_IDX(HC_REMOVE_VDEV)] = {
.handler = hcall_remove_vdev}, .handler = hcall_remove_vdev},
[HC_IDX(HC_ASSIGN_PIO_REGION)] = {
.handler = hcall_assign_pio_region},
[HC_IDX(HC_DEASSIGN_PIO_REGION)] = {
.handler = hcall_deassign_pio_region},
[HC_IDX(HC_SET_PTDEV_INTR_INFO)] = { [HC_IDX(HC_SET_PTDEV_INTR_INFO)] = {
.handler = hcall_set_ptdev_intr_info}, .handler = hcall_set_ptdev_intr_info},
[HC_IDX(HC_RESET_PTDEV_INTR_INFO)] = { [HC_IDX(HC_RESET_PTDEV_INTR_INFO)] = {

View File

@ -204,7 +204,7 @@ void allow_guest_pio_access(struct acrn_vm *vm, uint16_t port_address,
b = (uint32_t *)vm->arch_vm.io_bitmap; b = (uint32_t *)vm->arch_vm.io_bitmap;
for (i = 0U; i < nbytes; i++) { for (i = 0U; i < nbytes; i++) {
b[address >> 5U] &= ~(1U << (address & 0x1fU)); bitmap32_clear_nolock(address & 0x1fU, &b[address >> 5U]);
address++; address++;
} }
} }
@ -218,7 +218,35 @@ void deny_guest_pio_access(struct acrn_vm *vm, uint16_t port_address,
b = (uint32_t *)vm->arch_vm.io_bitmap; b = (uint32_t *)vm->arch_vm.io_bitmap;
for (i = 0U; i < nbytes; i++) { for (i = 0U; i < nbytes; i++) {
b[address >> 5U] |= (1U << (address & 0x1fU)); bitmap32_set_nolock(address & 0x1fU, &b[address >> 5U]);
address++; address++;
} }
} }
/**
* @brief Check if a VM has full access to a port I/O range
*
* This API check if given \p vm has direct access to the port I/O space
* starting from \p port_address to \p port_address + \p nbytes - 1.
*
* @param vm The VM whose port I/O access permissions is to be checked
* @param port_address The start address of the port I/O range
* @param nbytes The size of the range, in bytes
*/
bool has_direct_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_t nbytes)
{
uint16_t address = port_address;
uint32_t *b;
uint32_t i;
bool ret = true;
b = (uint32_t *)vm->arch_vm.io_bitmap;
for (i = 0U; i < nbytes; i++) {
if (bitmap32_test(address & 0x1fU, &b[address >> 5U])) {
ret = false;
break;
}
address++;
}
return ret;
}

View File

@ -1354,3 +1354,75 @@ int32_t hcall_remove_vdev(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, __u
} }
return ret; return ret;
} }
/**
* @brief Assign PIO device resource to a VM.
*
* @param vcpu Pointer to vCPU that initiates the hypercall
* @param target_vm Pointer to target VM data structure
* @param param2 guest physical address. This gpa points to data structure of
* acrn_pio_region including assign PIO resource info
*
* @pre is_service_vm(vcpu->vm)
* @return 0 on success, non-zero on error.
*/
int32_t hcall_assign_pio_region(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm,
__unused uint64_t param1, uint64_t param2)
{
struct acrn_vm *vm = vcpu->vm;
int32_t ret = -EINVAL;
struct acrn_pio_region pio_region;
/* We should only assign a device to a post-launched VM at creating time for safety, not runtime or other cases*/
if (is_created_vm(target_vm)) {
if (copy_from_gpa(vm, &pio_region, param2, sizeof(pio_region)) == 0) {
if (has_direct_pio_access(vm, pio_region.res.port_address, pio_region.res.size)) {
deny_guest_pio_access(vm, pio_region.res.port_address, pio_region.res.size);
allow_guest_pio_access(target_vm, pio_region.res.port_address, pio_region.res.size);
ret = 0;
} else {
pr_err("vm[%d] %s failed: pio region not fully accessible for vm[%d].\n",target_vm->vm_id, __func__, vm->vm_id);
}
}
} else {
pr_err("vm[%d] %s failed: target vm not created.\n",target_vm->vm_id, __func__);
}
return ret;
}
/**
* @brief Deassign PIO device resource from a VM.
*
* @param vcpu Pointer to vCPU that initiates the hypercall
* @param target_vm Pointer to target VM data structure
* @param param2 guest physical address. This gpa points to data structure of
* acrn_pio_region including deassign PIO device info
*
* @pre is_service_vm(vcpu->vm)
* @return 0 on success, non-zero on error.
*/
int32_t hcall_deassign_pio_region(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm,
__unused uint64_t param1, uint64_t param2)
{
struct acrn_vm *vm = vcpu->vm;
int32_t ret = -EINVAL;
struct acrn_pio_region pio_region;
/* We should only de-assign a device from a post-launched VM at creating/shutdown/reset time */
if ((is_paused_vm(target_vm) || is_created_vm(target_vm))) {
if (copy_from_gpa(vm, &pio_region, param2, sizeof(pio_region)) == 0) {
if (has_direct_pio_access(target_vm, pio_region.res.port_address, pio_region.res.size)) {
deny_guest_pio_access(target_vm, pio_region.res.port_address, pio_region.res.size);
allow_guest_pio_access(vm, pio_region.res.port_address, pio_region.res.size);
ret = 0;
} else {
pr_err("vm[%d] %s failed: pio region not fully accessible for vm[%d].\n",vm->vm_id, __func__, target_vm->vm_id);
}
}
} else {
pr_err("vm[%d] %s failed: target vm not created or not paused.\n",target_vm->vm_id, __func__);
}
return ret;
}

View File

@ -80,6 +80,18 @@ void allow_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_
*/ */
void deny_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_t nbytes); void deny_guest_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_t nbytes);
/**
* @brief Check if a VM has full access to a port I/O range
*
* This API check if given \p vm has direct access to the port I/O space
* starting from \p port_address to \p port_address + \p nbytes - 1.
*
* @param vm The VM whose port I/O access permissions is to be checked
* @param port_address The start address of the port I/O range
* @param nbytes The size of the range, in bytes
*/
bool has_direct_pio_access(struct acrn_vm *vm, uint16_t port_address, uint32_t nbytes);
/** /**
* @brief Fire HSM interrupt to Service VM * @brief Fire HSM interrupt to Service VM
*/ */

View File

@ -346,6 +346,34 @@ int32_t hcall_add_vdev(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, uint64
*/ */
int32_t hcall_remove_vdev(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2); int32_t hcall_remove_vdev(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2);
/**
* @brief Assign PIO device resource to VM.
*
* @param vcpu Pointer to vCPU that initiates the hypercall
* @param target_vm Pointer to target VM data structure
* @param param1 not used
* @param param2 guest physical address. This gpa points to data structure of
* acrn_mmiores including assign PIO device resource info
*
* @pre is_service_vm(vcpu->vm)
* @return 0 on success, non-zero on error.
*/
int32_t hcall_assign_pio_region(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2);
/**
* @brief Deassign PIO device resource from VM.
*
* @param vcpu Pointer to vCPU that initiates the hypercall
* @param target_vm Pointer to target VM data structure
* @param param1 not used
* @param param2 guest physical address. This gpa points to data structure of
* acrn_mmiores including assign PIO device resource info
*
* @pre is_service_vm(vcpu->vm)
* @return 0 on success, non-zero on error.
*/
int32_t hcall_deassign_pio_region(struct acrn_vcpu *vcpu, struct acrn_vm *target_vm, uint64_t param1, uint64_t param2);
/** /**
* @brief Set interrupt mapping info of ptdev. * @brief Set interrupt mapping info of ptdev.
* *

View File

@ -707,6 +707,19 @@ struct acrn_mmiodev {
} res[MMIODEV_RES_NUM]; } res[MMIODEV_RES_NUM];
}; };
/**
* @brief Info to assign or deassign a PIO device for a VM
*/
struct acrn_pio_region {
char name[8];
struct acrn_piores {
/** the io port address for PIO device */
uint16_t port_address;
/** the size of the io port for the PIO resource */
uint16_t size;
} res;
};
/** /**
* @brief Info to create or destroy a virtual PCI or legacy device for a VM * @brief Info to create or destroy a virtual PCI or legacy device for a VM
* *

View File

@ -74,6 +74,8 @@
#define HC_DEASSIGN_MMIODEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x08UL) #define HC_DEASSIGN_MMIODEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x08UL)
#define HC_ADD_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x09UL) #define HC_ADD_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x09UL)
#define HC_REMOVE_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0AUL) #define HC_REMOVE_VDEV BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0AUL)
#define HC_ASSIGN_PIO_REGION BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0BUL)
#define HC_DEASSIGN_PIO_REGION BASE_HC_ID(HC_ID, HC_ID_PCI_BASE + 0x0CUL)
/* DEBUG */ /* DEBUG */
#define HC_ID_DBG_BASE 0x60UL #define HC_ID_DBG_BASE 0x60UL