mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-22 13:37:10 +00:00
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:
parent
fb2acbacb1
commit
b138dd1eaf
@ -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)] = {
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user