mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
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:
committed by
Quentin Perret
parent
5747f9215c
commit
ace1a8e103
27
arch/arm64/include/asm/io-mpt-s2mpu.h
Normal file
27
arch/arm64/include/asm/io-mpt-s2mpu.h
Normal 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__ */
|
||||
@@ -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
|
||||
|
||||
103
arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c
Normal file
103
arch/arm64/kvm/hyp/nvhe/iommu/io-mpt-s2mpu.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user