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 <smostafa@google.com>
Signed-off-by: Quentin Perret <qperret@google.com>
This commit is contained in:
Mostafa Saleh
2022-11-15 10:38:33 +00:00
committed by Quentin Perret
parent 5747f9215c
commit ace1a8e103
4 changed files with 150 additions and 63 deletions

View File

@@ -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 <linux/bitfield.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_s2mpu.h>
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__ */

View File

@@ -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

View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 - Google LLC
*/
#include <asm/io-mpt-s2mpu.h>
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;
}

View File

@@ -18,6 +18,7 @@
#include <nvhe/mm.h>
#include <nvhe/spinlock.h>
#include <nvhe/trap_handler.h>
#include <asm/io-mpt-s2mpu.h>
#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);