ANDROID: KVM: arm64: s2mpu: Move SFR init to EL2

S2MPU code previously assumed that all S2MPUs were powered on at boot
and would check the version register and precompute the value of
S2MPU.CONTEXT_CFG_VALID_VID.

With EL1 S2MPU code being removed, and to allow for S2MPUs not powered
at boot, move the code to EL2 and run it on resume.

Bug: 190463801
Change-Id: Icaccfd125a6be7bab336ca3ffee52f2a33cf43b2
Signed-off-by: David Brazdil <dbrazdil@google.com>
(cherry picked from commit c823243791)
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
David Brazdil
2022-01-07 11:33:38 +00:00
committed by Mostafa Saleh
parent d2e5766252
commit 23fba443b3
2 changed files with 95 additions and 93 deletions

View File

@@ -30,6 +30,10 @@
#define for_each_powered_s2mpu(i) \
for_each_s2mpu((i)) if (is_powered_on((i)))
#define CTX_CFG_ENTRY(ctxid, nr_ctx, vid) \
(CONTEXT_CFG_VALID_VID_CTX_VID(ctxid, vid) \
| (((ctxid) < (nr_ctx)) ? CONTEXT_CFG_VALID_VID_CTX_VALID(ctxid) : 0))
size_t __ro_after_init kvm_hyp_nr_s2mpus;
struct s2mpu __ro_after_init *kvm_hyp_s2mpus;
struct mpt kvm_hyp_host_mpt;
@@ -67,18 +71,75 @@ static bool is_in_power_domain(struct s2mpu *dev, u64 power_domain_id)
}
}
/*
* Write CONTEXT_CFG_VALID_VID configuration before touching L1ENTRY* registers.
* Writes to those registers are ignored unless there is a context ID allocated
* to the corresponding VID (v9 only).
*/
static void __set_context_ids(struct s2mpu *dev)
static u32 __context_cfg_valid_vid(struct s2mpu *dev, u32 vid_bmap)
{
if (!is_version(dev, S2MPU_VERSION_9))
return;
u8 ctx_vid[NR_CTX_IDS] = { 0 };
unsigned int vid, ctx = 0;
unsigned int num_ctx;
u32 res;
writel_relaxed(dev->context_cfg_valid_vid,
dev->va + REG_NS_CONTEXT_CFG_VALID_VID);
/* Only initialize once. */
if (dev->context_cfg_valid_vid)
return dev->context_cfg_valid_vid;
num_ctx = readl_relaxed(dev->va + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK;
while (vid_bmap) {
/* Break if we cannot allocate more. */
if (ctx >= num_ctx)
break;
vid = __ffs(vid_bmap);
vid_bmap &= ~BIT(vid);
ctx_vid[ctx++] = vid;
}
/* The following loop was unrolled so bitmasks are constant. */
BUILD_BUG_ON(NR_CTX_IDS != 8);
res = CTX_CFG_ENTRY(0, ctx, ctx_vid[0])
| CTX_CFG_ENTRY(1, ctx, ctx_vid[1])
| CTX_CFG_ENTRY(2, ctx, ctx_vid[2])
| CTX_CFG_ENTRY(3, ctx, ctx_vid[3])
| CTX_CFG_ENTRY(4, ctx, ctx_vid[4])
| CTX_CFG_ENTRY(5, ctx, ctx_vid[5])
| CTX_CFG_ENTRY(6, ctx, ctx_vid[6])
| CTX_CFG_ENTRY(7, ctx, ctx_vid[7]);
dev->context_cfg_valid_vid = res;
return res;
}
static int __initialize_v9(struct s2mpu *dev)
{
u32 ssmt_valid_vid_bmap, ctx_cfg;
/* Assume all VIDs may be generated by the connected SSMTs for now. */
ssmt_valid_vid_bmap = ALL_VIDS_BITMAP;
ctx_cfg = __context_cfg_valid_vid(dev, ssmt_valid_vid_bmap);
if (!ctx_cfg)
return -EINVAL;
/*
* Write CONTEXT_CFG_VALID_VID configuration before touching L1ENTRY*
* registers. Writes to those registers are ignored unless there is
* a context ID allocated to the corresponding VID (v9 only).
*/
writel_relaxed(ctx_cfg, dev->va + REG_NS_CONTEXT_CFG_VALID_VID);
return 0;
}
static int __initialize(struct s2mpu *dev)
{
if (!dev->version)
dev->version = readl_relaxed(dev->va + REG_NS_VERSION);
switch (dev->version & VERSION_CHECK_MASK) {
case S2MPU_VERSION_8:
return 0;
case S2MPU_VERSION_9:
return __initialize_v9(dev);
default:
return -EINVAL;
}
}
static void __set_control_regs(struct s2mpu *dev)
@@ -179,12 +240,14 @@ static void __set_l1entry_l2table_addr(struct s2mpu *dev, unsigned int gb,
* Initialize S2MPU device and set all GB regions to 1G granularity with
* given protection bits.
*/
static void initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot)
static int initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot)
{
unsigned int gb, vid;
int ret;
/* Must write CONTEXT_CFG_VALID_VID before setting L1ENTRY registers. */
__set_context_ids(dev);
ret = __initialize(dev);
if (ret)
return ret;
for_each_gb_and_vid(gb, vid)
__set_l1entry_attr_with_prot(dev, gb, vid, prot);
@@ -192,19 +255,22 @@ static void initialize_with_prot(struct s2mpu *dev, enum mpt_prot prot)
/* Set control registers, enable the S2MPU. */
__set_control_regs(dev);
return 0;
}
/*
* Initialize S2MPU device, set L2 table addresses and configure L1TABLE_ATTR
* registers according to the given MPT struct.
*/
static void initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt)
static int initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt)
{
unsigned int gb, vid;
struct fmpt *fmpt;
int ret;
/* Must write CONTEXT_CFG_VALID_VID before setting L1ENTRY registers. */
__set_context_ids(dev);
ret = __initialize(dev);
if (ret)
return ret;
for_each_gb_and_vid(gb, vid) {
fmpt = &mpt->fmpt[gb];
@@ -215,6 +281,7 @@ static void initialize_with_mpt(struct s2mpu *dev, struct mpt *mpt)
/* Set control registers, enable the S2MPU. */
__set_control_regs(dev);
return 0;
}
/*
@@ -326,6 +393,7 @@ static bool s2mpu_host_smc_handler(struct kvm_cpu_context *host_ctxt)
struct arm_smccc_res res;
struct s2mpu *dev;
int ret;
if (fn != SMC_CMD_PREPARE_PD_ONOFF)
return false; /* SMC not handled */
@@ -350,23 +418,25 @@ static bool s2mpu_host_smc_handler(struct kvm_cpu_context *host_ctxt)
hyp_spin_lock(&s2mpu_lock);
arm_smccc_1_1_smc(fn, mode, domain_id, group, &res);
if (res.a0 == SMCCC_RET_SUCCESS) {
ret = res.a0;
if (ret == SMCCC_RET_SUCCESS) {
for_each_s2mpu(dev) {
if (!is_in_power_domain(dev, domain_id))
continue;
if (mode == SMC_MODE_POWER_UP) {
dev->power_state = S2MPU_POWER_ON;
initialize_with_mpt(dev, &kvm_hyp_host_mpt);
ret = initialize_with_mpt(dev, &kvm_hyp_host_mpt);
} else {
initialize_with_prot(dev, MPT_PROT_NONE);
ret = initialize_with_prot(dev, MPT_PROT_NONE);
dev->power_state = S2MPU_POWER_OFF;
}
}
}
hyp_spin_unlock(&s2mpu_lock);
cpu_reg(host_ctxt, 0) = res.a0;
cpu_reg(host_ctxt, 0) = ret;
return true; /* SMC handled */
}
@@ -469,8 +539,11 @@ static int s2mpu_init(void)
* Program all S2MPUs powered on at boot. Note that they may not be in
* the blocking reset state as the bootloader may have programmed them.
*/
for_each_powered_s2mpu(dev)
initialize_with_mpt(dev, &kvm_hyp_host_mpt);
for_each_powered_s2mpu(dev) {
ret = initialize_with_mpt(dev, &kvm_hyp_host_mpt);
if (ret)
return ret;
}
return 0;
}

View File

@@ -13,10 +13,6 @@
#include <asm/kvm_mmu.h>
#include <asm/kvm_s2mpu.h>
#define CTX_CFG_ENTRY(ctxid, nr_ctx, vid) \
(CONTEXT_CFG_VALID_VID_CTX_VID(ctxid, vid) \
| (((ctxid) < (nr_ctx)) ? CONTEXT_CFG_VALID_VID_CTX_VALID(ctxid) : 0))
struct s2mpu_irq_info {
struct device *dev;
void __iomem *va;
@@ -81,59 +77,6 @@ static irqreturn_t s2mpu_irq_handler(int irq, void *data)
return ret;
}
static u32 gen_ctx_cfg_valid_vid(struct platform_device *pdev,
unsigned int num_ctx, u32 vid_bmap)
{
u8 ctx_vid[NR_CTX_IDS] = { 0 };
unsigned int vid, ctx = 0;
/* Check NUM_CONTEXT value is within bounds. This should not happen. */
if (WARN_ON(num_ctx > NR_CTX_IDS))
num_ctx = NR_CTX_IDS;
while (vid_bmap) {
/* Break if we cannot allocate more. */
if (ctx >= num_ctx) {
dev_warn(&pdev->dev,
"could not allocate all context IDs, DMA may be blocked (VID bitmap: 0x%x)",
vid_bmap);
break;
}
vid = __ffs(vid_bmap);
vid_bmap &= ~BIT(vid);
ctx_vid[ctx++] = vid;
}
/* The following loop was unrolled so bitmasks are constant. */
BUILD_BUG_ON(NR_CTX_IDS != 8);
return CTX_CFG_ENTRY(0, ctx, ctx_vid[0])
| CTX_CFG_ENTRY(1, ctx, ctx_vid[1])
| CTX_CFG_ENTRY(2, ctx, ctx_vid[2])
| CTX_CFG_ENTRY(3, ctx, ctx_vid[3])
| CTX_CFG_ENTRY(4, ctx, ctx_vid[4])
| CTX_CFG_ENTRY(5, ctx, ctx_vid[5])
| CTX_CFG_ENTRY(6, ctx, ctx_vid[6])
| CTX_CFG_ENTRY(7, ctx, ctx_vid[7]);
}
static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr,
struct s2mpu *info)
{
unsigned int num_ctx;
u32 ssmt_valid_vid_bmap;
ssmt_valid_vid_bmap = ALL_VIDS_BITMAP;
num_ctx = readl_relaxed(kaddr + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK;
info->context_cfg_valid_vid = gen_ctx_cfg_valid_vid(pdev, num_ctx, ssmt_valid_vid_bmap);
if (!info->context_cfg_valid_vid) {
dev_err(&pdev->dev, "failed to allocate context IDs");
return -EINVAL;
}
return 0;
}
/*
* Parse interrupt information from DT and if found, register IRQ handler.
* This is considered optional and will not fail even if the initialization is
@@ -232,20 +175,6 @@ static int s2mpu_probe(struct platform_device *pdev)
*/
s2mpu_probe_irq(pdev, kaddr);
info->version = readl_relaxed(kaddr + REG_NS_VERSION);
switch (info->version & VERSION_CHECK_MASK) {
case S2MPU_VERSION_8:
break;
case S2MPU_VERSION_9:
ret = s2mpu_probe_v9(pdev, kaddr, info);
if (ret)
return ret;
break;
default:
dev_err(&pdev->dev, "unexpected version 0x%08x", info->version);
return -EINVAL;
}
/* Insert successfully parsed devices to a list later copied to hyp. */
list_add_tail(&entry->list, &s2mpu_list);
kvm_hyp_nr_s2mpus++;