From ace1a8e10342eca8730f747c504c79798ee5586d Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Tue, 15 Nov 2022 10:38:33 +0000 Subject: [PATCH] ANDROID: KVM: arm64: s2mpu: Abstract page table ops Create a seprate file where S2MPU page table operations are allocated based on configuration This will be helpful when supporting S2MPU versions that have different SMPT and FMPT formats Bug: 255731794 Change-Id: I0b80a329d4357511baf6847baf84dc4644bb835f Signed-off-by: Mostafa Saleh Signed-off-by: Quentin Perret --- arch/arm64/include/asm/io-mpt-s2mpu.h | 27 +++++ arch/arm64/kvm/hyp/nvhe/Makefile | 1 + arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c | 103 +++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c | 82 ++++----------- 4 files changed, 150 insertions(+), 63 deletions(-) create mode 100644 arch/arm64/include/asm/io-mpt-s2mpu.h create mode 100644 arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c diff --git a/arch/arm64/include/asm/io-mpt-s2mpu.h b/arch/arm64/include/asm/io-mpt-s2mpu.h new file mode 100644 index 000000000000..382422b26ed6 --- /dev/null +++ b/arch/arm64/include/asm/io-mpt-s2mpu.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 - Google LLC + */ + +#ifndef __IO_MPT_S2MPU_H__ +#define __IO_MPT_S2MPU_H__ + +#include +#include +#include + +struct s2mpu_mpt_cfg { + enum s2mpu_version version; +}; + +struct s2mpu_mpt_ops { + void (*init_with_prot)(void *dev_va, enum mpt_prot prot); + void (*init_with_mpt)(void *dev_va, struct mpt *mpt); + void (*apply_range)(void *dev_va, struct mpt *mpt, u32 first_gb, u32 last_gb); + void (*prepare_range)(struct mpt *mpt, phys_addr_t first_byte, + phys_addr_t last_byte, enum mpt_prot prot); +}; + +const struct s2mpu_mpt_ops *s2mpu_get_mpt_ops(struct s2mpu_mpt_cfg cfg); + +#endif /* __IO_MPT_S2MPU_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index 7772aedd9b2b..01fd26cff95a 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -29,6 +29,7 @@ hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o hyp-obj-y += $(lib-objs) hyp-obj-$(CONFIG_KVM_S2MPU) += iommu/s2mpu.o +hyp-obj-$(CONFIG_KVM_S2MPU) += iommu/io-mpt-s2mpu.o ## ## Build rules for compiling nVHE hyp code diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c new file mode 100644 index 000000000000..5ec941d842a5 --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 - Google LLC + */ + +#include + +static void __set_l1entry_attr_with_prot(void *dev_va, unsigned int gb, + unsigned int vid, enum mpt_prot prot) +{ + writel_relaxed(L1ENTRY_ATTR_1G(prot), + dev_va + REG_NS_L1ENTRY_ATTR(vid, gb)); +} + +static void __set_l1entry_attr_with_fmpt(void *dev_va, unsigned int gb, + unsigned int vid, struct fmpt *fmpt) +{ + if (fmpt->gran_1g) { + __set_l1entry_attr_with_prot(dev_va, gb, vid, fmpt->prot); + } else { + /* Order against writes to the SMPT. */ + writel(L1ENTRY_ATTR_L2(SMPT_GRAN_ATTR), + dev_va + REG_NS_L1ENTRY_ATTR(vid, gb)); + } +} + +static void __set_l1entry_l2table_addr(void *dev_va, unsigned int gb, + unsigned int vid, phys_addr_t addr) +{ + /* Order against writes to the SMPT. */ + writel(L1ENTRY_L2TABLE_ADDR(addr), + dev_va + REG_NS_L1ENTRY_L2TABLE_ADDR(vid, gb)); +} + +static void init_with_prot(void *dev_va, enum mpt_prot prot) +{ + unsigned int gb, vid; + + for_each_gb_and_vid(gb, vid) + __set_l1entry_attr_with_prot(dev_va, gb, vid, prot); +} + +static void init_with_mpt(void *dev_va, struct mpt *mpt) +{ + unsigned int gb, vid; + struct fmpt *fmpt; + + for_each_gb_and_vid(gb, vid) { + fmpt = &mpt->fmpt[gb]; + __set_l1entry_l2table_addr(dev_va, gb, vid, __hyp_pa(fmpt->smpt)); + __set_l1entry_attr_with_fmpt(dev_va, gb, vid, fmpt); + } +} + +static void apply_range(void *dev_va, struct mpt *mpt, u32 first_gb, u32 last_gb) +{ + unsigned int gb, vid; + struct fmpt *fmpt; + + for_each_gb_in_range(gb, first_gb, last_gb) { + fmpt = &mpt->fmpt[gb]; + if (fmpt->flags & MPT_UPDATE_L1) { + for_each_vid(vid) + __set_l1entry_attr_with_fmpt(dev_va, gb, vid, fmpt); + } + } +} + +static void prepare_range(struct mpt *mpt, phys_addr_t first_byte, + phys_addr_t last_byte, enum mpt_prot prot) +{ + unsigned int first_gb = first_byte / SZ_1G; + unsigned int last_gb = last_byte / SZ_1G; + size_t start_gb_byte, end_gb_byte; + unsigned int gb; + struct fmpt *fmpt; + + for_each_gb_in_range(gb, first_gb, last_gb) { + fmpt = &mpt->fmpt[gb]; + start_gb_byte = (gb == first_gb) ? first_byte % SZ_1G : 0; + end_gb_byte = (gb == last_gb) ? (last_byte % SZ_1G) + 1 : SZ_1G; + + __set_fmpt_range(fmpt, start_gb_byte, end_gb_byte, prot); + + if (fmpt->flags & MPT_UPDATE_L2) + kvm_flush_dcache_to_poc(fmpt->smpt, SMPT_SIZE); + } +} + +static const struct s2mpu_mpt_ops this_ops = { + .init_with_prot = init_with_prot, + .init_with_mpt = init_with_mpt, + .apply_range = apply_range, + .prepare_range = prepare_range, +}; + +const struct s2mpu_mpt_ops *s2mpu_get_mpt_ops(struct s2mpu_mpt_cfg cfg) +{ + if ((cfg.version == S2MPU_VERSION_8) || (cfg.version == S2MPU_VERSION_9)) + return &this_ops; + + return NULL; +} diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index aefbcd7a4750..b76d2cef6919 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -18,6 +18,7 @@ #include #include #include +#include #define SMC_CMD_PREPARE_PD_ONOFF 0x82000410 #define SMC_MODE_POWER_UP 1 @@ -40,6 +41,7 @@ struct s2mpu_drv_data { u32 context_cfg_valid_vid; }; +static const struct s2mpu_mpt_ops *mpt_ops; static struct mpt host_mpt; const struct pkvm_iommu_ops pkvm_s2mpu_ops; @@ -264,48 +266,19 @@ static void __range_invalidation_init(struct pkvm_iommu *dev, phys_addr_t first_ __invalidation_barrier_init(dev); } -static void __set_l1entry_attr_with_prot(struct pkvm_iommu *dev, unsigned int gb, - unsigned int vid, enum mpt_prot prot) -{ - writel_relaxed(L1ENTRY_ATTR_1G(prot), - dev->va + REG_NS_L1ENTRY_ATTR(vid, gb)); -} - -static void __set_l1entry_attr_with_fmpt(struct pkvm_iommu *dev, unsigned int gb, - unsigned int vid, struct fmpt *fmpt) -{ - if (fmpt->gran_1g) { - __set_l1entry_attr_with_prot(dev, gb, vid, fmpt->prot); - } else { - /* Order against writes to the SMPT. */ - writel(L1ENTRY_ATTR_L2(SMPT_GRAN_ATTR), - dev->va + REG_NS_L1ENTRY_ATTR(vid, gb)); - } -} - -static void __set_l1entry_l2table_addr(struct pkvm_iommu *dev, unsigned int gb, - unsigned int vid, phys_addr_t addr) -{ - /* Order against writes to the SMPT. */ - writel(L1ENTRY_L2TABLE_ADDR(addr), - dev->va + REG_NS_L1ENTRY_L2TABLE_ADDR(vid, gb)); -} - /* * Initialize S2MPU device and set all GB regions to 1G granularity with * given protection bits. */ static int initialize_with_prot(struct pkvm_iommu *dev, enum mpt_prot prot) { - unsigned int gb, vid; int ret; ret = __initialize(dev); if (ret) return ret; - for_each_gb_and_vid(gb, vid) - __set_l1entry_attr_with_prot(dev, gb, vid, prot); + mpt_ops->init_with_prot(dev->va, prot); __all_invalidation(dev); /* Set control registers, enable the S2MPU. */ @@ -319,19 +292,13 @@ static int initialize_with_prot(struct pkvm_iommu *dev, enum mpt_prot prot) */ static int initialize_with_mpt(struct pkvm_iommu *dev, struct mpt *mpt) { - unsigned int gb, vid; - struct fmpt *fmpt; int ret; ret = __initialize(dev); if (ret) return ret; - for_each_gb_and_vid(gb, vid) { - fmpt = &mpt->fmpt[gb]; - __set_l1entry_l2table_addr(dev, gb, vid, __hyp_pa(fmpt->smpt)); - __set_l1entry_attr_with_fmpt(dev, gb, vid, fmpt); - } + mpt_ops->init_with_mpt(dev->va, mpt); __all_invalidation(dev); /* Set control registers, enable the S2MPU. */ @@ -361,22 +328,7 @@ static bool to_valid_range(phys_addr_t *start, phys_addr_t *end) static void __mpt_idmap_prepare(struct mpt *mpt, phys_addr_t first_byte, phys_addr_t last_byte, enum mpt_prot prot) { - unsigned int first_gb = first_byte / SZ_1G; - unsigned int last_gb = last_byte / SZ_1G; - size_t start_gb_byte, end_gb_byte; - unsigned int gb; - struct fmpt *fmpt; - - for_each_gb_in_range(gb, first_gb, last_gb) { - fmpt = &mpt->fmpt[gb]; - start_gb_byte = (gb == first_gb) ? first_byte % SZ_1G : 0; - end_gb_byte = (gb == last_gb) ? (last_byte % SZ_1G) + 1 : SZ_1G; - - __set_fmpt_range(fmpt, start_gb_byte, end_gb_byte, prot); - - if (fmpt->flags & MPT_UPDATE_L2) - kvm_flush_dcache_to_poc(fmpt->smpt, SMPT_SIZE); - } + mpt_ops->prepare_range(mpt, first_byte, last_byte, prot); } static void __mpt_idmap_apply(struct pkvm_iommu *dev, struct mpt *mpt, @@ -384,17 +336,8 @@ static void __mpt_idmap_apply(struct pkvm_iommu *dev, struct mpt *mpt, { unsigned int first_gb = first_byte / SZ_1G; unsigned int last_gb = last_byte / SZ_1G; - unsigned int gb, vid; - struct fmpt *fmpt; - for_each_gb_in_range(gb, first_gb, last_gb) { - fmpt = &mpt->fmpt[gb]; - - if (fmpt->flags & MPT_UPDATE_L1) { - for_each_vid(vid) - __set_l1entry_attr_with_fmpt(dev, gb, vid, fmpt); - } - } + mpt_ops->apply_range(dev->va, mpt, first_gb, last_gb); /* Initiate invalidation, completed in __mdt_idmap_complete. */ __range_invalidation_init(dev, first_byte, last_byte); } @@ -536,6 +479,7 @@ static int s2mpu_init(void *data, size_t size) phys_addr_t pa; unsigned int gb; int ret = 0; + struct s2mpu_mpt_cfg cfg; if (size != sizeof(in_mpt)) return -EINVAL; @@ -543,6 +487,18 @@ static int s2mpu_init(void *data, size_t size) /* The host can concurrently modify 'data'. Copy it to avoid TOCTOU. */ memcpy(&in_mpt, data, sizeof(in_mpt)); + /* + * Only v8/v9 are supported at this point so hardcode the version + * as there is not way to get the version required from the kernel yet, + * v8/v9 are compatible so using any of them will work. + */ + cfg.version = S2MPU_VERSION_8; + /* Get page table operations for this version. */ + mpt_ops = s2mpu_get_mpt_ops(cfg); + /* If version is wrong return. */ + if (!mpt_ops) + return -EINVAL; + /* Take ownership of all SMPT buffers. This will also map them in. */ for_each_gb(gb) { smpt = kern_hyp_va(in_mpt.fmpt[gb].smpt);