ANDROID: KVM: arm64: Enable S2MPUs in __pkvm_init_stage2_iommu

Initialize the S2MPU driver in __pkvm_init_stage2_iommu if requested by
the host. The driver sets kvm_iommu_ops and configures all S2MPUs which
are powered on at that point (ie. all S2MPUs on currently supported
devices).

The S2MPU L1ENTRY registers are set to 1G granularity and R/W access.
CTRL0/CTRL1/CFG as set to reasonable defaults, though the code relies on
the reset state blocking all traffic as well.

On fault the S2MPUs are configured to return SLVERR/DECERR (v8/9) to the
master. Interrupts are enabled for all VIDs and trigger an IRQ handler
if EL1 init registered a handler as a result of a DT interrupts entry.

Because the host can configure the SSMTs freely, all permission bits are
configured for all VIDs. For v9 CONTEXT_CFG_VALID_VIDS is set to the
value precomputed at EL1, allocating a context ID to each VID.

Test: builds, boots
Bug: 190463801
Signed-off-by: David Brazdil <dbrazdil@google.com>
Change-Id: I20e658a77c93fa18a07e388d13a639dc67e600d8
This commit is contained in:
David Brazdil
2021-06-23 09:18:32 +00:00
parent 0ed6ef5469
commit f6b90bb6f5
3 changed files with 194 additions and 5 deletions

View File

@@ -9,18 +9,41 @@
#include <linux/bitfield.h>
#define S2MPU_MMIO_SIZE SZ_64K
#define NR_VIDS 8
#define NR_CTX_IDS 8
#define ALL_VIDS_BITMAP GENMASK(NR_VIDS - 1, 0)
#define REG_NS_CTRL0 0x0
#define REG_NS_CTRL1 0x4
#define REG_NS_CFG 0x10
#define REG_NS_INTERRUPT_ENABLE_PER_VID_SET 0x20
#define REG_NS_INTERRUPT_CLEAR 0x2c
#define REG_NS_VERSION 0x60
#define REG_NS_NUM_CONTEXT 0x100
#define REG_NS_CONTEXT_CFG_VALID_VID 0x104
#define REG_NS_ALL_INVALIDATION 0x1000
#define REG_NS_FAULT_STATUS 0x2000
#define REG_NS_FAULT_PA_LOW(vid) (0x2004 + ((vid) * 0x20))
#define REG_NS_FAULT_PA_HIGH(vid) (0x2008 + ((vid) * 0x20))
#define REG_NS_FAULT_INFO(vid) (0x2010 + ((vid) * 0x20))
#define REG_NS_L1ENTRY_ATTR(vid, gb) (0x4004 + ((vid) * 0x200) + ((gb) * 0x8))
#define CTRL0_ENABLE BIT(0)
#define CTRL0_INTERRUPT_ENABLE BIT(1)
#define CTRL0_FAULT_RESP_TYPE_SLVERR BIT(2) /* for v8 */
#define CTRL0_FAULT_RESP_TYPE_DECERR BIT(2) /* for v9 */
#define CTRL1_DISABLE_CHK_S1L1PTW BIT(0)
#define CTRL1_DISABLE_CHK_S1L2PTW BIT(1)
#define CTRL1_ENABLE_PAGE_SIZE_AWARENESS BIT(2)
#define CTRL1_DISABLE_CHK_USER_MATCHED_REQ BIT(3)
#define CFG_MPTW_CACHE_OVERRIDE BIT(0)
#define CFG_MPTW_QOS_OVERRIDE BIT(8)
#define CFG_MPTW_SHAREABLE BIT(16)
/* For use with hi_lo_readq_relaxed(). */
#define REG_NS_FAULT_PA_HIGH_LOW(vid) REG_NS_FAULT_PA_LOW(vid)
@@ -41,6 +64,8 @@
#define CONTEXT_CFG_VALID_VID_CTX_VID(ctx, vid) \
FIELD_PREP(GENMASK((4 * (ctx) + 2), 4 * (ctx)), (vid))
#define INVALIDATION_INVALIDATE BIT(0)
#define NR_FAULT_INFO_REGS 8
#define FAULT_INFO_VID_MASK GENMASK(26, 24)
#define FAULT_INFO_TYPE_MASK GENMASK(23, 21)
@@ -51,6 +76,25 @@
#define FAULT_INFO_LEN_MASK GENMASK(19, 16)
#define FAULT_INFO_ID_MASK GENMASK(15, 0)
#define L1ENTRY_ATTR_PROT(prot) FIELD_PREP(GENMASK(2, 1), prot)
#define L1ENTRY_ATTR_1G(prot) L1ENTRY_ATTR_PROT(prot)
#define NR_GIGABYTES 64
#define RO_GIGABYTES_FIRST 4
#define RO_GIGABYTES_LAST 33
/*
* Iterate over S2MPU gigabyte regions. Skip those that cannot be modified
* (the MMIO registers are read only, with reset value MPT_PROT_NONE).
*/
#define for_each_gb_in_range(i, first, last) \
for ((i) = (first); (i) <= (last) && (i) < NR_GIGABYTES; \
(i) = (((i) + 1 == RO_GIGABYTES_FIRST) ? RO_GIGABYTES_LAST : (i)) + 1)
#define for_each_gb(i) for_each_gb_in_range(i, 0, NR_GIGABYTES - 1)
#define for_each_vid(i) for ((i) = 0; (i) < NR_VIDS; (i)++)
#define for_each_gb_and_vid(gb, vid) for_each_vid((vid)) for_each_gb((gb))
enum s2mpu_version {
S2MPU_VERSION_8 = 0x11000000,
S2MPU_VERSION_9 = 0x20000000,
@@ -71,6 +115,14 @@ struct s2mpu {
u32 context_cfg_valid_vid;
};
enum mpt_prot {
MPT_PROT_NONE = 0,
MPT_PROT_R = BIT(0),
MPT_PROT_W = BIT(1),
MPT_PROT_RW = MPT_PROT_R | MPT_PROT_W,
MPT_PROT_MASK = MPT_PROT_RW,
};
extern size_t kvm_nvhe_sym(kvm_hyp_nr_s2mpus);
#define kvm_hyp_nr_s2mpus kvm_nvhe_sym(kvm_hyp_nr_s2mpus)

View File

@@ -6,9 +6,148 @@
#include <linux/kvm_host.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_hyp.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_s2mpu.h>
size_t __ro_after_init kvm_hyp_nr_s2mpus;
struct s2mpu __ro_after_init *kvm_hyp_s2mpus;
#include <nvhe/mm.h>
const struct kvm_iommu_ops kvm_s2mpu_ops = (struct kvm_iommu_ops){};
#define for_each_s2mpu(i) \
for ((i) = &kvm_hyp_s2mpus[0]; (i) != &kvm_hyp_s2mpus[kvm_hyp_nr_s2mpus]; (i)++)
#define for_each_powered_s2mpu(i) \
for_each_s2mpu((i)) if (is_powered_on((i)))
size_t __ro_after_init kvm_hyp_nr_s2mpus;
struct s2mpu __ro_after_init *kvm_hyp_s2mpus;
static bool is_version(struct s2mpu *dev, u32 version)
{
return (dev->version & VERSION_CHECK_MASK) == version;
}
static bool is_powered_on(struct s2mpu *dev)
{
switch (dev->power_state) {
case S2MPU_POWER_ALWAYS_ON:
case S2MPU_POWER_ON:
return true;
case S2MPU_POWER_OFF:
return false;
default:
BUG();
}
}
/*
* 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)
{
if (!is_version(dev, S2MPU_VERSION_9))
return;
writel_relaxed(dev->context_cfg_valid_vid,
dev->va + REG_NS_CONTEXT_CFG_VALID_VID);
}
static void __set_control_regs(struct s2mpu *dev)
{
u32 ctrl0 = 0, irq_vids;
/*
* Note: We set the values of CTRL0, CTRL1 and CFG registers here but we
* still rely on the correctness of their reset values. S2MPUs *must*
* reset to a state where all DMA traffic is blocked until the hypervisor
* writes its configuration to the S2MPU. A malicious EL1 could otherwise
* attempt to bypass the permission checks in the window between powering
* on the S2MPU and this function being called.
*/
/* Enable the S2MPU, otherwise all traffic would be allowed through. */
ctrl0 |= CTRL0_ENABLE;
/*
* Enable interrupts on fault for all VIDs. The IRQ must also be
* specified in DT to get unmasked in the GIC.
*/
ctrl0 |= CTRL0_INTERRUPT_ENABLE;
irq_vids = ALL_VIDS_BITMAP;
/* Return SLVERR/DECERR to device on permission fault. */
ctrl0 |= is_version(dev, S2MPU_VERSION_9) ? CTRL0_FAULT_RESP_TYPE_DECERR
: CTRL0_FAULT_RESP_TYPE_SLVERR;
writel_relaxed(irq_vids, dev->va + REG_NS_INTERRUPT_ENABLE_PER_VID_SET);
writel_relaxed(0, dev->va + REG_NS_CFG);
writel_relaxed(0, dev->va + REG_NS_CTRL1);
writel_relaxed(ctrl0, dev->va + REG_NS_CTRL0);
}
static void __all_invalidation(struct s2mpu *dev)
{
writel_relaxed(INVALIDATION_INVALIDATE,
dev->va + REG_NS_ALL_INVALIDATION);
}
static void __set_l1entry_attr_with_prot(struct s2mpu *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));
}
/**
* 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)
{
unsigned int gb, vid;
/* Must write CONTEXT_CFG_VALID_VID before setting L1ENTRY registers. */
__set_context_ids(dev);
for_each_gb_and_vid(gb, vid)
__set_l1entry_attr_with_prot(dev, gb, vid, prot);
__all_invalidation(dev);
/* Set control registers, enable the S2MPU. */
__set_control_regs(dev);
}
static int s2mpu_init(void)
{
struct s2mpu *dev;
int ret;
/* Map data structures in EL2 stage-1. */
ret = pkvm_create_mappings(kvm_hyp_s2mpus,
kvm_hyp_s2mpus + kvm_hyp_nr_s2mpus,
PAGE_HYP);
if (ret)
return ret;
/* Map S2MPU MMIO regions in EL2 stage-1. */
for_each_s2mpu(dev) {
dev->va = (void __iomem *)__pkvm_create_private_mapping(
dev->pa, S2MPU_MMIO_SIZE, PAGE_HYP_DEVICE);
if (IS_ERR_OR_NULL(dev->va))
return PTR_ERR(dev->va);
}
/*
* 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_prot(dev, MPT_PROT_RW);
return 0;
}
const struct kvm_iommu_ops kvm_s2mpu_ops = (struct kvm_iommu_ops){
.init = s2mpu_init,
};

View File

@@ -12,8 +12,6 @@
#include <asm/kvm_mmu.h>
#include <asm/kvm_s2mpu.h>
#define S2MPU_MMIO_SIZE SZ_64K
#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))