diff --git a/arch/arm64/include/asm/kvm_tmi.h b/arch/arm64/include/asm/kvm_tmi.h index c82671a23b6d59349e133ef6cd99cdba80e7eab6..11eacf161a02b6ab5951c96ef79c50707dfa0eff 100644 --- a/arch/arm64/include/asm/kvm_tmi.h +++ b/arch/arm64/include/asm/kvm_tmi.h @@ -31,6 +31,7 @@ #define TMI_ERROR_INTERNAL 11 #define TMI_ERROR_CVM_POWEROFF 12 #define TMI_ERROR_TTT_CREATED 13 +#define TMI_ERROR_TTT_DESTROY_AGAIN 14 #define TMI_RETURN_STATUS(ret) ((ret) & 0xFF) #define TMI_RETURN_INDEX(ret) (((ret) >> 8) & 0xFF) @@ -230,6 +231,7 @@ struct tmi_tec_run { #define TMI_FNUM_FEATURES U(0x26C) #define TMI_FNUM_TTT_MAP_RANGE U(0x26D) #define TMI_FNUM_TTT_UNMAP_RANGE U(0x26E) +#define TMI_FNUM_TTT_DESTROY U(0x26F) #define TMI_FNUM_INF_TEST U(0x270) #define TMI_FNUM_KAE_INIT U(0x273) #define TMI_FNUM_KAE_ENABLE U(0x274) @@ -266,6 +268,7 @@ struct tmi_tec_run { #define TMI_TMM_MEM_INFO_SHOW TMI_FID(SMC_64, TMI_FNUM_MEM_INFO_SHOW) #define TMI_TMM_TTT_MAP_RANGE TMI_FID(SMC_64, TMI_FNUM_TTT_MAP_RANGE) #define TMI_TMM_TTT_UNMAP_RANGE TMI_FID(SMC_64, TMI_FNUM_TTT_UNMAP_RANGE) +#define TMI_TMM_TTT_DESTROY TMI_FID(SMC_64, TMI_FNUM_TTT_DESTROY) #define TMI_TMM_INF_TEST TMI_FID(SMC_64, TMI_FNUM_INF_TEST) #define TMI_TMM_KAE_INIT TMI_FID(SMC_64, TMI_FNUM_KAE_INIT) #define TMI_TMM_KAE_ENABLE TMI_FID(SMC_64, TMI_FNUM_KAE_ENABLE) @@ -392,6 +395,7 @@ u64 tmi_ttt_create(u64 numa_set, u64 rd, u64 map_addr, u64 level); u64 tmi_psci_complete(u64 calling_tec, u64 target_tec); u64 tmi_features(u64 index); u64 tmi_ttt_map_range(u64 rd, u64 map_addr, u64 size, u64 cur_node, u64 target_node); +u64 tmi_ttt_destroy(u64 rd); u64 tmi_mem_info_show(u64 mem_info_addr); u64 tmi_dev_ttt_create(u64 numa_set, u64 rd, u64 map_addr, u64 level); @@ -413,6 +417,7 @@ u64 tmi_kae_init(void); u64 tmi_kae_enable(u64 rd, u64 numa_set, u64 is_enable); u64 mmio_va_to_pa(void *addr); +int virtcca_io_mem_abort(struct kvm_vcpu *vcpu, unsigned long hva, phys_addr_t fault_ipa); void kvm_cvm_vcpu_put(struct kvm_vcpu *vcpu); int kvm_load_user_data(struct kvm *kvm, unsigned long arg); unsigned long cvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu, diff --git a/arch/arm64/include/asm/kvm_tmm.h b/arch/arm64/include/asm/kvm_tmm.h index 5dab4b141d2eae5e9333daf0c4b7398c894bd2de..f6e773c4aa13cf192243d5ed139a5e196dbac452 100644 --- a/arch/arm64/include/asm/kvm_tmm.h +++ b/arch/arm64/include/asm/kvm_tmm.h @@ -75,8 +75,19 @@ struct virtcca_cvm { u32 cvm_vmid; u64 rd; u64 loader_start; +#ifndef __GENKSYMS__ + union { + u64 image_end; + u64 mmio_start; + }; + union { + u64 initrd_start; + u64 mmio_end; + }; +#else u64 image_end; u64 initrd_start; +#endif u64 dtb_end; u64 ram_size; struct kvm_numa_info numa_info; diff --git a/arch/arm64/include/asm/virtcca_coda.h b/arch/arm64/include/asm/virtcca_coda.h index 1a000c1d6f13b44f01d766fd32c291be4eed7658..1522c13c65563fefee36b86e1eec27c0fbaed22a 100644 --- a/arch/arm64/include/asm/virtcca_coda.h +++ b/arch/arm64/include/asm/virtcca_coda.h @@ -24,8 +24,16 @@ #define SMMU_DOMAIN_IS_SAME 0x2 -int virtcca_attach_secure_dev(struct iommu_domain *domain, struct iommu_group *group, +enum cc_dev_type { + CC_DEV_NONE_TYPE, /* No assigned CC devices */ + CC_DEV_HOST_TYPE, /* CC devices assigned to the host */ + CC_DEV_NVM_TYPE, /* CC devices assigned to normal vm */ + CC_DEV_CVM_TYPE, /* CC devices assigned to confidential vm */ +}; + +int virtcca_attach_dev(struct iommu_domain *domain, struct iommu_group *group, bool iommu_secure); +void virtcca_detach_dev(struct iommu_domain *domain, struct iommu_group *group); u64 virtcca_get_iommu_device_msi_addr(struct iommu_group *iommu_group); int virtcca_iommu_group_set_dev_msi_addr(struct iommu_group *iommu_group, unsigned long *iova); @@ -81,6 +89,8 @@ u64 get_g_cc_dev_msi_addr(u32 sid); void set_g_cc_dev_msi_addr(u32 sid, u64 msi_addr); +u32 get_g_coda_dev_vm_type(u32 sid); + void g_cc_dev_table_init(void); u32 virtcca_tmi_dev_attach(struct arm_smmu_domain *arm_smmu_domain, struct kvm *kvm); @@ -88,5 +98,22 @@ u32 virtcca_tmi_dev_attach(struct arm_smmu_domain *arm_smmu_domain, struct kvm * void virtcca_iommu_dma_get_msi_page(void *cookie, dma_addr_t *iova, phys_addr_t *phys); int virtcca_msi_map(struct vfio_pci_core_device *vdev); + +static inline u8 virtcca_readb(void __iomem *addr, struct pci_dev *pdev) +{ + return tmi_mmio_read(mmio_va_to_pa(addr), CVM_RW_8_BIT, pci_dev_id(pdev)); +} + +static inline u16 virtcca_readw(void __iomem *addr, struct pci_dev *pdev) +{ + return tmi_mmio_read(mmio_va_to_pa(addr), CVM_RW_16_BIT, pci_dev_id(pdev)); +} + +static inline u32 virtcca_readl(void __iomem *addr, struct pci_dev *pdev) +{ + return tmi_mmio_read(mmio_va_to_pa(addr), CVM_RW_32_BIT, pci_dev_id(pdev)); +} +size_t virtcca_pci_get_rom_size(void *pdev, void __iomem *rom, + size_t size); #endif #endif diff --git a/arch/arm64/include/asm/virtcca_cvm_host.h b/arch/arm64/include/asm/virtcca_cvm_host.h index 8913a07d0bd14cb63f4f0c3420c1296c46e3db38..c379ad17cffef29a0c85991451d5fe7c77e5b782 100644 --- a/arch/arm64/include/asm/virtcca_cvm_host.h +++ b/arch/arm64/include/asm/virtcca_cvm_host.h @@ -7,6 +7,9 @@ #ifdef CONFIG_HISI_VIRTCCA_HOST +#define UEFI_LOADER_START 0x0 +#define UEFI_SIZE 0x8000000 + bool is_virtcca_cvm_enable(void); #else diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 68efca6ef24e7a4faf0c96ebfe421b51ec16bd97..65554248cb7f3482c5789f2961b1bf14966261e6 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1763,6 +1763,12 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu) goto out_unlock; } +#ifdef CONFIG_HISI_VIRTCCA_CODA + ret = virtcca_io_mem_abort(vcpu, hva, fault_ipa); + if (ret != -EPERM) + goto out_unlock; +#endif + /* Userspace should not be able to register out-of-bounds IPAs */ VM_BUG_ON(fault_ipa >= kvm_phys_size(vcpu->kvm)); diff --git a/arch/arm64/kvm/tmi.c b/arch/arm64/kvm/tmi.c index 90742a62e896fd640e2bd677315655e9fa99847b..fecd0ee1a96080aa7d8ed7d307bd3bbe1da0cc5d 100644 --- a/arch/arm64/kvm/tmi.c +++ b/arch/arm64/kvm/tmi.c @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include /** * mmio_va_to_pa - To convert the virtual address of the mmio space @@ -37,6 +40,22 @@ u64 mmio_va_to_pa(void *addr) } EXPORT_SYMBOL(mmio_va_to_pa); +int virtcca_io_mem_abort(struct kvm_vcpu *vcpu, unsigned long hva, phys_addr_t fault_ipa) +{ + struct virtcca_cvm *cvm = vcpu->kvm->arch.virtcca_cvm; + + if (!vcpu_is_tec(vcpu) || !(fault_ipa >= cvm->mmio_start && fault_ipa < cvm->mmio_end)) + return -EPERM; + + if (kvm_is_error_hva(hva) && kvm_vcpu_dabt_is_cm(vcpu)) { + kvm_incr_pc(vcpu); + return 1; + } + + fault_ipa |= kvm_vcpu_get_hfar(vcpu) & ((1 << 12) - 1); + return io_mem_abort(vcpu, fault_ipa); +} + u64 tmi_version(void) { struct arm_smccc_res res; @@ -144,6 +163,14 @@ u64 tmi_ttt_map_range(u64 rd, u64 map_addr, u64 size, u64 cur_node, u64 target_n return res.a1; } +u64 tmi_ttt_destroy(u64 rd) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_smc(TMI_TMM_TTT_DESTROY, rd, &res); + return res.a1; +} + /* Used to create smmu command queue and event queue */ u64 tmi_smmu_queue_create(u64 params_ptr) { diff --git a/arch/arm64/kvm/virtcca_cvm.c b/arch/arm64/kvm/virtcca_cvm.c index 43f61e5be40be7ef452ccef7c983fee5431009fb..cb2ee7e4fe7694a68a03008f21d1c89e78983180 100644 --- a/arch/arm64/kvm/virtcca_cvm.c +++ b/arch/arm64/kvm/virtcca_cvm.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,9 @@ static DEFINE_SPINLOCK(cvm_vmid_lock); static unsigned long *cvm_vmid_bitmap; DEFINE_STATIC_KEY_FALSE(virtcca_cvm_is_available); #define SIMD_PAGE_SIZE 0x3000 - +#define UEFI_MAX_SIZE 0x8000000 +#define UEFI_DTB_START 0x40000000 +#define DTB_MAX_SIZE 0x200000 int kvm_enable_virtcca_cvm(struct kvm *kvm) { if (!static_key_enabled(&virtcca_cvm_is_available)) @@ -179,6 +182,7 @@ int kvm_arm_create_cvm(struct kvm *kvm) void kvm_destroy_cvm(struct kvm *kvm) { struct virtcca_cvm *cvm = kvm->arch.virtcca_cvm; + int ret; uint32_t cvm_vmid; #ifdef CONFIG_HISI_VIRTCCA_CODA struct arm_smmu_domain *arm_smmu_domain; @@ -213,6 +217,18 @@ void kvm_destroy_cvm(struct kvm *kvm) if (tmi_kae_enable(cvm->rd, numa_set, 0)) kvm_err("vf destroy failed!\n"); + do { + touch_nmi_watchdog(); + ret = tmi_ttt_destroy(cvm->rd); + } while (ret == TMI_ERROR_TTT_DESTROY_AGAIN); + + /* + * Considering that lower versions of TMM do not support + * the tmi_ttt_destroy interface. + */ + if (ret) + pr_warn("KVM destroy cVM ttt failed\n"); + if (!tmi_cvm_destroy(cvm->rd)) kvm_info("KVM has destroyed cVM: %d\n", cvm->cvm_vmid); @@ -641,8 +657,7 @@ static int kvm_populate_ipa_cvm_range(struct kvm *kvm, !IS_ALIGNED(args->populate_ipa_size2, PAGE_SIZE)) return -EINVAL; - if (args->populate_ipa_base1 < cvm->loader_start || - args->populate_ipa_base2 < args->populate_ipa_base1 + args->populate_ipa_size1 || + if (args->populate_ipa_base2 < args->populate_ipa_base1 + args->populate_ipa_size1 || cvm->dtb_end < args->populate_ipa_base2 + args->populate_ipa_size2) return -EINVAL; @@ -651,6 +666,20 @@ static int kvm_populate_ipa_cvm_range(struct kvm *kvm, ipa_base1 = round_down(args->populate_ipa_base1, l2_granule); ipa_end2 = round_up(args->populate_ipa_base2 + args->populate_ipa_size2, l2_granule); + /* uefi boot, uefi image and uefi ram from 0 to 128M */ + if (ipa_base1 == UEFI_LOADER_START) { + phys_addr_t ipa_base2 = round_down(args->populate_ipa_base2, l2_granule); + phys_addr_t ipa_end1 = round_up(args->populate_ipa_base1 + + args->populate_ipa_size1, l2_granule); + int uefi_ret = kvm_populate_ram_region(kvm, l2_granule, ipa_base1, ipa_end1, args); + + if (!uefi_ret) { + uefi_ret = kvm_populate_ram_region(kvm, l2_granule, ipa_base2, ipa_end2, + args); + } + return uefi_ret; + } + /* direct boot */ return kvm_populate_ram_region(kvm, l2_granule, ipa_base1, ipa_end2, args); } @@ -735,11 +764,6 @@ int kvm_tec_enter(struct kvm_vcpu *vcpu) if (READ_ONCE(cvm->state) != CVM_STATE_ACTIVE) return -EINVAL; - if (cpumask_weight(¤t->cpus_mask) > 1) { - kvm_err("cvm only support running with binding cpu\n"); - return -EINVAL; - } - run = tec->tec_run; /* set/clear TWI TWE flags */ if (vcpu->arch.hcr_el2 & HCR_TWI) @@ -792,6 +816,15 @@ int kvm_init_tmm(void) return 0; } +u64 virtcca_get_tmi_version(void) +{ + u64 res = tmi_version(); + + if (res == SMCCC_RET_NOT_SUPPORTED) + return 0; + return res; +} + static bool is_numa_ipa_range_valid(struct kvm_numa_info *numa_info) { unsigned long i; @@ -812,6 +845,11 @@ static bool is_numa_ipa_range_valid(struct kvm_numa_info *numa_info) return true; } +static inline bool is_dtb_info_has_extend_data(u64 dtb_info) +{ + return dtb_info & 0x1; +} + int kvm_load_user_data(struct kvm *kvm, unsigned long arg) { struct kvm_user_data user_data; @@ -836,25 +874,39 @@ int kvm_load_user_data(struct kvm *kvm, unsigned long arg) if (!is_numa_ipa_range_valid(numa_info)) return -EINVAL; - if (user_data.loader_start < numa_node->ipa_start || - user_data.dtb_end > ipa_end) + + if ((user_data.loader_start != numa_node->ipa_start) || + (user_data.data_start + user_data.data_size < user_data.data_start)) return -EINVAL; + + if (is_dtb_info_has_extend_data(user_data.dtb_info)) { + /* Direct boot, check DTB address is in IPA range */ + if (user_data.data_start + user_data.data_size > ipa_end) + return -EINVAL; + } else { + /* + * UEFI boot, check MMIO address range is within the valid limit (less than + * loader_start) + */ + if (user_data.data_start + user_data.data_size > user_data.loader_start) + return -EINVAL; + } + for (i = 0; i < numa_info->numa_cnt; i++) total_size += numa_info->numa_nodes[i].ipa_size; if (total_size != user_data.ram_size) return -EINVAL; } - if (user_data.image_end <= user_data.loader_start || - user_data.initrd_start < user_data.image_end || - user_data.dtb_end < user_data.initrd_start || - user_data.ram_size < user_data.dtb_end - user_data.loader_start) - return -EINVAL; + if (is_dtb_info_has_extend_data(user_data.dtb_info)) + cvm->dtb_end = user_data.data_start + user_data.data_size; + else { + cvm->dtb_end = user_data.loader_start + user_data.dtb_info; + cvm->mmio_start = user_data.data_start; + cvm->mmio_end = user_data.data_start + user_data.data_size; + } cvm->loader_start = user_data.loader_start; - cvm->image_end = user_data.image_end; - cvm->initrd_start = user_data.initrd_start; - cvm->dtb_end = user_data.dtb_end; cvm->ram_size = user_data.ram_size; memcpy(&cvm->numa_info, numa_info, sizeof(struct kvm_numa_info)); diff --git a/drivers/coda/coda.c b/drivers/coda/coda.c index 8272a09e5807c5685e8f0b6fe8ad5e4da34a2352..137e902082fe57da3722693d93c02094be8d82ef 100644 --- a/drivers/coda/coda.c +++ b/drivers/coda/coda.c @@ -6,6 +6,12 @@ #include #include +/* The lock during the operation of the CoDA mananged devices linked list */ +static DEFINE_SPINLOCK(coda_dev_lock); + +/* Protect root port status from racing */ +static DEFINE_SPINLOCK(pcipc_enable_lock); + struct cc_dev_config { u32 sid; /* BDF number of the device */ u32 vmid; /* virtual machine id */ @@ -14,6 +20,7 @@ struct cc_dev_config { /* MSI addr for confidential device with iommu group granularity */ u64 msi_addr; struct hlist_node node; /* device hash table */ + u32 vm_type; /* 0:none; 1:host; 2:nvm; 3:cvm */ }; static DEFINE_HASHTABLE(g_cc_dev_htable, MAX_CC_DEV_NUM_ORDER); @@ -113,38 +120,67 @@ static int get_sibling_devices(struct device *dev, uint16_t *devs, int max_devs) * @vmid: Virtual machine id * @root_bd: Root port bus device num * @secure: Whether the device is secure or not + * @vm_type: Device ownership * * Returns: * %0 if add obj success * %-ENOMEM if alloc obj failed */ -static int add_cc_dev_obj(u32 sid, u32 vmid, u32 root_bd, bool secure) +static int add_cc_dev_obj(u32 sid, u32 vmid, u32 root_bd, bool secure, u32 vm_type) { struct cc_dev_config *obj; + spin_lock(&coda_dev_lock); hash_for_each_possible(g_cc_dev_htable, obj, node, sid) { if (obj->sid == sid) { obj->vmid = vmid; obj->root_bd = root_bd; obj->secure = secure; obj->msi_addr = 0; + obj->vm_type = vm_type; + spin_unlock(&coda_dev_lock); return 0; } } obj = kzalloc(sizeof(*obj), GFP_KERNEL); - if (!obj) + if (!obj) { + spin_unlock(&coda_dev_lock); return -ENOMEM; + } obj->sid = sid; obj->vmid = vmid; obj->root_bd = root_bd; obj->secure = secure; + obj->vm_type = vm_type; hash_add(g_cc_dev_htable, &obj->node, sid); + spin_unlock(&coda_dev_lock); return 0; } +/** + * delete_coda_dev_obj - Delete device obj to CoDA hash table + * @sid: Stream id of dev + * + */ +static void delete_coda_dev_obj(u32 sid) +{ + struct cc_dev_config *obj; + + spin_lock(&coda_dev_lock); + hash_for_each_possible(g_cc_dev_htable, obj, node, sid) { + if (obj != NULL && obj->sid == sid) { + hash_del(&obj->node); + kfree(obj); + spin_unlock(&coda_dev_lock); + return; + } + } + spin_unlock(&coda_dev_lock); +} + /** * is_cc_root_bd - Whether the root port is secure or not * @root_bd: Root port bus device num @@ -394,7 +430,7 @@ static inline int virtcca_delegate_secure_dev(uint16_t root_bd, struct arm_smmu_ } for (i = 0; i < params->num_dev; i++) { - ret = add_cc_dev_obj(params->devs[i], 0, root_bd, true); + ret = add_cc_dev_obj(params->devs[i], 0, root_bd, true, CC_DEV_CVM_TYPE); if (ret) break; } @@ -434,13 +470,58 @@ static inline int add_secure_dev_to_cc_table(struct arm_smmu_device *smmu, dev_err(smmu->dev, "CoDA: sid is not cc dev\n"); return -EINVAL; } - ret = add_cc_dev_obj(sid, smmu_domain->s2_cfg.vmid, root_bd, true); + ret = add_cc_dev_obj(sid, smmu_domain->s2_cfg.vmid, root_bd, true, CC_DEV_CVM_TYPE); if (ret) break; } return ret; } +u32 get_g_coda_dev_vm_type(u32 sid) +{ + struct cc_dev_config *obj; + u32 vm_type = CC_DEV_NONE_TYPE; + + spin_lock(&coda_dev_lock); + hash_for_each_possible(g_cc_dev_htable, obj, node, sid) { + if (obj != NULL && obj->sid == sid) { + vm_type = obj->vm_type; + spin_unlock(&coda_dev_lock); + return vm_type; + } + } + + spin_unlock(&coda_dev_lock); + return vm_type; +} + +static bool virtcca_check_dev_is_assigned_to_nvm(struct tmi_dev_delegate_params *params) +{ + for (int i = 0; i < params->num_dev; i++) { + if (get_g_coda_dev_vm_type(params->devs[i]) == CC_DEV_NVM_TYPE) { + pr_err("CoDA: device sid 0x%x has already been assigned to nvm\n", + params->devs[i]); + return true; + } + } + return false; +} + +static int virtcca_get_all_cc_dev_info(struct device *dev, struct tmi_dev_delegate_params *params) +{ + int ret = 0; + uint16_t root_bd = get_root_bd(dev); + + params->root_bd = root_bd; + params->num_dev = get_sibling_devices(dev, params->devs, MAX_DEV_PER_PORT); + if (params->num_dev >= MAX_DEV_PER_PORT) { + ret = -EINVAL; + pr_err("%s nums overflow\n", __func__); + return ret; + } + return ret; +} + /** * virtcca_enable_secure_dev - Enable the PCIe protection controller function * of the security device @@ -459,15 +540,29 @@ static int virtcca_enable_secure_dev(struct arm_smmu_domain *smmu_domain, u64 ret = 0; uint16_t root_bd = get_root_bd(dev); struct arm_smmu_device *smmu = smmu_domain->smmu; + struct tmi_dev_delegate_params *params = kzalloc(sizeof(*params), GFP_KERNEL); + + if (!params) + return -ENOMEM; if (!is_cc_root_bd(root_bd)) { + ret = virtcca_get_all_cc_dev_info(dev, params); + if (ret) + goto out; + + ret = virtcca_check_dev_is_assigned_to_nvm(params); + if (ret) + goto out; + ret = virtcca_delegate_secure_dev(root_bd, smmu, dev); if (ret) - return ret; + goto out; } ret = add_secure_dev_to_cc_table(smmu, smmu_domain, root_bd, master); +out: + kfree(params); return ret; } @@ -510,7 +605,9 @@ int virtcca_secure_dev_operator(struct device *dev, void *domain) return -EINVAL; } + spin_lock(&pcipc_enable_lock); ret = virtcca_enable_secure_dev(smmu_domain, master, dev); + spin_unlock(&pcipc_enable_lock); if (ret) return ret; @@ -533,31 +630,148 @@ int virtcca_secure_dev_operator(struct device *dev, void *domain) EXPORT_SYMBOL_GPL(virtcca_secure_dev_operator); /** - * virtcca_attach_secure_dev - Attach the device of iommu - * group to confidential virtual machine - * @domain: The handle of iommu domain - * @group: Iommu group - * @iommu_secure : Whether the iommu is secure or not + * virtcca_attach_each_dev_to_nvm - Attach each device under the same group to nvm + * @dev: the struct of device + * @domain: The handle of iommu_domain * * Returns: - * %0 if attach the all devices success - * %-EINVAL if the smmu does not initialize secure state - * %-ENOMEM if the device create secure ste failed - * %-ENOENT if the device does not have fwspec + * %0 if the device is not a CC device + * %-EINVAL if the device is a CC device, CC device is not allowed to assign to the normal VM */ -int virtcca_attach_secure_dev(struct iommu_domain *domain, struct iommu_group *group, - bool iommu_secure) +static int virtcca_attach_each_dev_to_nvm(struct device *dev, void *domain) { + int i, j; + u32 sid = 0; int ret = 0; + uint16_t root_bd; + struct arm_smmu_device *smmu = NULL; + struct arm_smmu_master *master = NULL; if (!is_virtcca_cvm_enable()) - return ret; + return 0; + + master = dev_iommu_priv_get(dev); + smmu = master->smmu; - if (!iommu_secure) + if (!virtcca_smmu_enable(smmu)) + return 0; + + root_bd = get_root_bd(dev); + spin_lock(&pcipc_enable_lock); + if (is_cc_root_bd(root_bd)) { + dev_err(smmu->dev, + "CoDA: the security device under the root port 0x%x is not allowed to assign to \ + the normal VM\n", root_bd); + spin_unlock(&pcipc_enable_lock); + return -EINVAL; + } + spin_unlock(&pcipc_enable_lock); + + for (i = 0; i < master->num_streams; i++) { + sid = master->streams[i].id; + /* Bridged PCI devices may end up with duplicated IDs */ + for (j = 0; j < i; j++) + if (master->streams[j].id == sid) + break; + if (j < i) + continue; + + ret = add_cc_dev_obj(sid, 0, root_bd, false, CC_DEV_NVM_TYPE); + if (ret) + pr_err("CoDA: attach device to nvm, add device 0x%x to CoDA linked list \ + failed\n", sid); + } + + return 0; +} + +/** + * virtcca_detach_each_dev_from_vm - Detach each device under the same group from nvm or cvm + * 1NVM scenarioDelete the device information corresponding to the NVM from the CoDA management + * linked list + * + * 2CVM scenarioSet the device that has already been assigned to the CVM in the CoDA management + * linked list + * + * back to the host driver state and restore the device's STE stage 2 to the host driver + * @dev: The struct of device + * @domain: The handle of iommu_domain + * + */ +static int virtcca_detach_each_dev_from_vm(struct device *dev, void *domain) +{ + int i, j; + int ret = 0; + struct arm_smmu_device *smmu = NULL; + struct arm_smmu_master *master = NULL; + + if (!is_virtcca_cvm_enable()) + return 0; + + master = dev_iommu_priv_get(dev); + smmu = master->smmu; + + if (!virtcca_smmu_enable(smmu)) + return 0; + + for (i = 0; i < master->num_streams; i++) { + u32 sid = master->streams[i].id; + /* Bridged PCI devices may end up with duplicated IDs */ + for (j = 0; j < i; j++) + if (master->streams[j].id == sid) + break; + if (j < i) + continue; + + /* + * If the device is CC dev and is assigned to the cvm, + * we need call the tmi_dev_destroy to restore STE stage 2 to host driver + */ + if (is_cc_dev(sid)) { + if (get_g_coda_dev_vm_type(sid) == CC_DEV_CVM_TYPE) { + ret = add_cc_dev_obj(sid, 0, get_root_bd(dev), true, + CC_DEV_HOST_TYPE); + if (ret) + pr_err("CoDA: detach device from vm, add cc device 0x%x \ + failed\n", sid); + } + } else { + delete_coda_dev_obj(sid); + } + } + return 0; +} + +int virtcca_attach_dev(struct iommu_domain *domain, struct iommu_group *group, + bool iommu_secure) +{ + int ret = 0; + + if (!is_virtcca_cvm_enable()) return ret; - ret = iommu_group_for_each_dev(group, (void *)domain, virtcca_secure_dev_operator); + if (iommu_secure) + ret = iommu_group_for_each_dev(group, (void *)domain, virtcca_secure_dev_operator); + else + ret = iommu_group_for_each_dev(group, (void *)domain, + virtcca_attach_each_dev_to_nvm); return ret; } -EXPORT_SYMBOL_GPL(virtcca_attach_secure_dev); +EXPORT_SYMBOL_GPL(virtcca_attach_dev); + +/** + * virtcca_detach_dev - The VFIO driver calls this interface to + * detach the device from the VM + * @domain: The handle of iommu domain + * @group: Iommu group + * + */ +void virtcca_detach_dev(struct iommu_domain *domain, struct iommu_group *group) +{ + if (!is_virtcca_cvm_enable()) + return; + + iommu_group_for_each_dev(group, (void *)domain, virtcca_detach_each_dev_from_vm); +} +EXPORT_SYMBOL_GPL(virtcca_detach_dev); diff --git a/drivers/coda/coda_pci.c b/drivers/coda/coda_pci.c index 2b15fcbc6457177ee0cf8f4e18b010bba169e24e..3494da67d263e7335a74a4878d854e32baaf7213 100644 --- a/drivers/coda/coda_pci.c +++ b/drivers/coda/coda_pci.c @@ -212,3 +212,65 @@ u64 virtcca_pci_io_read(struct vfio_pci_core_device *vdev, return tmi_mmio_read(mmio_va_to_pa(io), size, pci_dev_id(pdev)); } EXPORT_SYMBOL_GPL(virtcca_pci_io_read); + +/** + * virtcca_pci_get_rom_size - obtain the actual size of the ROM image + * @pdev: target PCI device + * @rom: kernel virtual pointer to image of ROM + * @size: size of PCI window + * return: size of actual ROM image + * + * Determine the actual length of the ROM image. + * The PCI window size could be much larger than the + * actual image size. + */ +size_t virtcca_pci_get_rom_size(void *p, void __iomem *rom, size_t size) +{ + void __iomem *image; + int last_image; + unsigned int length; + struct pci_dev *pdev = (struct pci_dev *)p; + + if (!is_cc_dev(pci_dev_id(pdev))) + return 0; + image = rom; + do { + void __iomem *pds; + /* Standard PCI ROMs start out with these bytes 55 AA */ + if (virtcca_readw(image, pdev) != 0xAA55) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", + virtcca_readw(image, pdev)); + break; + } + /* get the PCI data structure and check its "PCIR" signature */ + pds = image + virtcca_readw(image + 24, pdev); + /* The PCIR data structure must begin on a 4-byte boundary */ + if (!IS_ALIGNED((unsigned long)pds, 4)) { + pci_info(pdev, "Invalid PCI ROM header signature: PCIR %#06x\n", + virtcca_readw(image + 24, pdev)); + break; + } + if (virtcca_readl(pds, pdev) != 0x52494350) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", + virtcca_readl(pds, pdev)); + break; + } + last_image = virtcca_readb(pds + 21, pdev) & 0x80; + length = virtcca_readw(pds + 16, pdev); + image += length * 512; + /* Avoid iterating through memory outside the resource window */ + if (image >= rom + size) + break; + if (!last_image) { + if (virtcca_readw(image, pdev) != 0xAA55) { + pci_info(pdev, "No more image in the PCI ROM\n"); + break; + } + } + } while (length && !last_image); + + /* never return a size larger than the PCI resource window */ + /* there are known ROMs that get the size wrong */ + return min((size_t)(image - rom), size); +} +EXPORT_SYMBOL_GPL(virtcca_pci_get_rom_size); diff --git a/drivers/coda/coda_vfio.c b/drivers/coda/coda_vfio.c index 9de5d50a6bffbe9ea8388a23064d078b3540d894..9c0fcfc759afd33b5ef951ee07aeda2429c70fa8 100644 --- a/drivers/coda/coda_vfio.c +++ b/drivers/coda/coda_vfio.c @@ -5,6 +5,7 @@ #include #include #include +#include #include /** @@ -53,9 +54,16 @@ int virtcca_map_pages(void *ops, unsigned long iova, kvm = smmu_domain->kvm; if (kvm) { struct virtcca_cvm *virtcca_cvm = kvm->arch.virtcca_cvm; + u64 uefi_start = UEFI_LOADER_START; + u64 uefi_end = UEFI_SIZE; loader_start = virtcca_cvm->loader_start; ram_size = virtcca_cvm->ram_size; + + /* Range [0x0, 0x8000000) was mapped in UEFI boot mode */ + if ((iova >= uefi_start && iova < uefi_end)) + goto skip; + /* Cvm ram space mapping*/ if (iova >= loader_start && iova < loader_start + ram_size && @@ -71,6 +79,7 @@ int virtcca_map_pages(void *ops, unsigned long iova, ret = cvm_map_unmap_ipa_range(kvm, iova, paddr, pgsize * pgcount, true); } +skip: if (mapped) *mapped += pgsize * pgcount; } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 9cdc28dc2cf8861e6141e2a85cb25fce3d43dea1..8e02783eabcba112bfc5f28171a2fa43e866598c 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1650,6 +1650,7 @@ struct iommu_domain *iommu_group_default_domain(struct iommu_group *group) { return group->default_domain; } +EXPORT_SYMBOL_GPL(iommu_group_default_domain); static int probe_iommu_group(struct device *dev, void *data) { diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 0fa6b3da63cc759e8934d1e52387f83eb8f4b3d7..120e9d4298bfeab6a9dc836c2e420caa3971daf1 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "pci.h" @@ -87,6 +88,13 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, int last_image; unsigned int length; + if (is_virtcca_cvm_enable()) { + int ret = virtcca_pci_get_rom_size(pdev, rom, size); + + if (ret != 0) + return ret; + } + image = rom; do { void __iomem *pds; diff --git a/drivers/vfio/container.c b/drivers/vfio/container.c index d53d08f169733c6413522ba5feee51a8f52e1d35..76d28506d7e9ea25c89ed8510f1cbc39bdc62269 100644 --- a/drivers/vfio/container.c +++ b/drivers/vfio/container.c @@ -12,6 +12,9 @@ #include #include #include +#ifdef CONFIG_HISI_VIRTCCA_CODA +#include +#endif #include "vfio.h" @@ -448,6 +451,11 @@ int vfio_container_attach_group(struct vfio_container *container, if (group->type == VFIO_IOMMU) iommu_group_release_dma_owner( group->iommu_group); +#ifdef CONFIG_HISI_VIRTCCA_CODA + if (group->type == VFIO_IOMMU) + virtcca_detach_dev(iommu_group_default_domain(group->iommu_group), + group->iommu_group); +#endif goto out_unlock_container; } } @@ -483,6 +491,12 @@ void vfio_group_detach_container(struct vfio_group *group) if (group->type == VFIO_IOMMU) iommu_group_release_dma_owner(group->iommu_group); +#ifdef CONFIG_HISI_VIRTCCA_CODA + if (group->type == VFIO_IOMMU) + virtcca_detach_dev(iommu_group_default_domain(group->iommu_group), + group->iommu_group); +#endif + group->container = NULL; group->container_users = 0; list_del(&group->container_next); diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index a47d08eab5f428737e47f00b0ceb87e54413475c..21b877638a88d7a1dc4d4757ea15bc6cf417fba6 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -1182,12 +1182,8 @@ static long virtcca_vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma * bool do_accounting) { struct kvm *kvm; - struct vfio_domain *domain, *d; + struct vfio_domain *d; LIST_HEAD(unmapped_region_list); - struct iommu_iotlb_gather iotlb_gather; - int unmapped_region_cnt = 0; - long unlocked = 0; - dma_addr_t iova = dma->iova, end = dma->iova + dma->size; if (!virtcca_check_is_cvm_or_not((void *)iommu, &kvm)) return 0; @@ -1198,72 +1194,14 @@ static long virtcca_vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma * if (list_empty(&iommu->domain_list)) return 0; - /* - * We use the IOMMU to track the physical addresses, otherwise we'd - * need a much more complicated tracking system. Unfortunately that - * means we need to use one of the iommu domains to figure out the - * pfns to unpin. The rest need to be unmapped in advance so we have - * no iommu translations remaining when the pages are unpinned. - */ - domain = d = list_first_entry(&iommu->domain_list, - struct vfio_domain, next); - - list_for_each_entry_continue(d, &iommu->domain_list, next) { + list_for_each_entry(d, &iommu->domain_list, next) { virtcca_iommu_unmap(d->domain, dma->iova, dma->size); cond_resched(); } - iommu_iotlb_gather_init(&iotlb_gather); - while (iova < end) { - size_t unmapped, len; - phys_addr_t phys, next; - - phys = iommu_iova_to_phys(domain->domain, iova); - if (WARN_ON(!phys)) { - iova += PAGE_SIZE; - continue; - } - - /* - * To optimize for fewer iommu_unmap() calls, each of which - * may require hardware cache flushing, try to find the - * largest contiguous physical memory chunk to unmap. - */ - for (len = PAGE_SIZE; - !domain->fgsp && iova + len < end; len += PAGE_SIZE) { - next = iommu_iova_to_phys(domain->domain, iova + len); - if (next != phys + len) - break; - } - - /* - * First, try to use fast unmap/unpin. In case of failure, - * switch to slow unmap/unpin path. - */ - unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys, - &unlocked, &unmapped_region_list, - &unmapped_region_cnt, - &iotlb_gather); - if (!unmapped) { - unmapped = unmap_unpin_slow(domain, dma, &iova, len, - phys, &unlocked); - if (WARN_ON(!unmapped)) - break; - } - } - dma->iommu_mapped = false; - if (unmapped_region_cnt) { - unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list, - &iotlb_gather); - } - - if (do_accounting) { - vfio_lock_acct(dma, -unlocked, true); - return 0; - } - return unlocked; + return 0; } #endif @@ -2775,7 +2713,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, if (!iommu_attach_group(d->domain, group->iommu_group)) { #ifdef CONFIG_HISI_VIRTCCA_CODA - ret = virtcca_attach_secure_dev(d->domain, + ret = virtcca_attach_dev(d->domain, group->iommu_group, iommu->secure); if (ret) goto out_domain; @@ -2795,7 +2733,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, } #ifdef CONFIG_HISI_VIRTCCA_CODA - ret = virtcca_attach_secure_dev(domain->domain, group->iommu_group, iommu->secure); + ret = virtcca_attach_dev(domain->domain, group->iommu_group, iommu->secure); if (ret) goto out_domain; #endif @@ -2833,6 +2771,9 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, out_detach: iommu_detach_group(domain->domain, group->iommu_group); +#ifdef CONFIG_HISI_VIRTCCA_CODA + virtcca_detach_dev(domain->domain, group->iommu_group); +#endif out_domain: iommu_domain_free(domain->domain); vfio_iommu_iova_free(&iova_copy); @@ -2994,6 +2935,9 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, continue; iommu_detach_group(domain->domain, group->iommu_group); +#ifdef CONFIG_HISI_VIRTCCA_CODA + virtcca_detach_dev(domain->domain, group->iommu_group); +#endif vfio_iommu_update_hwdbm(iommu, domain, false); update_dirty_scope = !group->pinned_page_dirty_scope; list_del(&group->next); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 56bbf59bda3cf6dd88ebe4dc461a6ac75df2853d..db917dd2a6a738ae86738f3aee7f97839e0cc73d 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "nfs4_fs.h" #include "callback.h" @@ -2411,7 +2412,12 @@ static int nfsiod_start(void) { struct workqueue_struct *wq; dprintk("RPC: creating workqueue nfsiod\n"); - wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); +#ifdef CONFIG_HISI_VIRTCCA_GUEST + if (unlikely(virtcca_cvm_domain())) + wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM, 0); + else +#endif + wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (wq == NULL) return -ENOMEM; nfsiod_workqueue = wq; diff --git a/include/linux/virtcca_cvm_domain.h b/include/linux/virtcca_cvm_domain.h index 41b2d15082ab3f6425f40438ae6357ff2dba08a9..5b6d6dfac05e80fbee8a62dbb8675000bc510b98 100644 --- a/include/linux/virtcca_cvm_domain.h +++ b/include/linux/virtcca_cvm_domain.h @@ -26,4 +26,34 @@ static inline void enable_swiotlb_for_cvm_dev(struct device *dev, bool enable) { #endif +#ifdef CONFIG_HISI_VIRTCCA_HOST + +bool is_virtcca_cvm_enable(void); +u64 virtcca_get_tmi_version(void); + +#else + +static inline bool is_virtcca_cvm_enable(void) +{ + return 0; +} + +static inline u64 virtcca_get_tmi_version(void) +{ + return 0; +} +#endif + +#ifdef CONFIG_HISI_VIRTCCA_CODA +size_t virtcca_pci_get_rom_size(void *pdev, void __iomem *rom, + size_t size); +#else +static inline size_t virtcca_pci_get_rom_size(void *pdev, void __iomem *rom, + size_t size) +{ + return 0; +} + +#endif + #endif /* __VIRTCCA_CVM_DOMAIN_H */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index c4984d20726e8814f8973df067565b569324a61c..27d3c8abd702f14b5a398e7a43916a86875f3bd0 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1523,9 +1523,15 @@ struct kvm_numa_info { struct kvm_user_data { __u64 loader_start; - __u64 image_end; - __u64 initrd_start; - __u64 dtb_end; + /* + * When the lowest bit of dtb_info is 0, the value of dtb_info represents the size of the + * DTB, and data_start and data_size represent the address base and size of the MMIO. + * When the lowest bit of dtb_info is 1, data_start and data_size represent the address base + * and size of the DTB. + */ + __u64 dtb_info; + __u64 data_start; + __u64 data_size; __u64 ram_size; struct kvm_numa_info numa_info; }; @@ -2430,4 +2436,7 @@ struct kvm_csv3_handle_memory { __u32 opcode; }; +/* get tmi version */ +#define KVM_GET_TMI_VERSION _IOR(KVMIO, 0xd2, u64) + #endif /* __LINUX_KVM_H */ diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index cef623ea150609cabf811f6945f74a9796c2dd60..dd3d410505059192985ac8148b642d35f9ffed6d 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1282,11 +1283,21 @@ static int rpciod_start(void) /* * Create the rpciod thread and wait for it to start. */ - wq = alloc_workqueue("rpciod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); +#ifdef CONFIG_HISI_VIRTCCA_GUEST + if (unlikely(virtcca_cvm_domain())) + wq = alloc_workqueue("rpciod", WQ_MEM_RECLAIM, 0); + else +#endif + wq = alloc_workqueue("rpciod", WQ_MEM_RECLAIM | WQ_UNBOUND, 0); if (!wq) goto out_failed; rpciod_workqueue = wq; - wq = alloc_workqueue("xprtiod", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); +#ifdef CONFIG_HISI_VIRTCCA_GUEST + if (unlikely(virtcca_cvm_domain())) + wq = alloc_workqueue("xprtiod", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + else +#endif + wq = alloc_workqueue("xprtiod", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); if (!wq) goto free_rpciod; xprtiod_workqueue = wq; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 421f253903ca89c1bc741dd564197182b305cbe0..4b7378445812f2f02e2f4291a9a45ba7f1d20b8d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -68,7 +68,7 @@ #include #include - +#include /* Worst case buffer size needed for holding an integer. */ #define ITOA_MAX_LEN 12 @@ -5158,6 +5158,17 @@ static long kvm_dev_ioctl(struct file *filp, goto out; r = KVM_API_VERSION; break; +#ifdef CONFIG_HISI_VIRTCCA_HOST + case KVM_GET_TMI_VERSION:{ + void __user *argp = (void __user *)arg; + u64 res = virtcca_get_tmi_version(); + + if (copy_to_user(argp, &res, sizeof(res))) + goto out; + r = 0; + break; + } +#endif case KVM_CREATE_VM: r = kvm_dev_ioctl_create_vm(arg); break;