mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
ANDROID: KVM: arm64: Copy S2MPU configuration to hyp
Create variables in hyp that will hold the DT information about S2MPUs to use by hyp at runtime. Copy the information from EL1 to EL2. The EL1 code computes the size of the data and allocates a sufficient number of pages, which hyp will later take ownership of. Test: builds, boots Bug: 190463801 Signed-off-by: David Brazdil <dbrazdil@google.com> Change-Id: I45ff22ea2049dd285bfcedbad3390baba9fc59a0
This commit is contained in:
@@ -57,9 +57,24 @@ enum s2mpu_version {
|
||||
};
|
||||
|
||||
enum s2mpu_power_state {
|
||||
S2MPU_POWER_ALWAYS_ON,
|
||||
S2MPU_POWER_ALWAYS_ON = 0,
|
||||
S2MPU_POWER_ON,
|
||||
S2MPU_POWER_OFF,
|
||||
};
|
||||
|
||||
struct s2mpu {
|
||||
phys_addr_t pa;
|
||||
void __iomem *va;
|
||||
u32 version;
|
||||
enum s2mpu_power_state power_state;
|
||||
u32 power_domain_id;
|
||||
u32 context_cfg_valid_vid;
|
||||
};
|
||||
|
||||
extern size_t kvm_nvhe_sym(kvm_hyp_nr_s2mpus);
|
||||
#define kvm_hyp_nr_s2mpus kvm_nvhe_sym(kvm_hyp_nr_s2mpus)
|
||||
|
||||
extern struct s2mpu *kvm_nvhe_sym(kvm_hyp_s2mpus);
|
||||
#define kvm_hyp_s2mpus kvm_nvhe_sym(kvm_hyp_s2mpus)
|
||||
|
||||
#endif /* __ARM64_KVM_S2MPU_H__ */
|
||||
|
||||
@@ -8,4 +8,7 @@
|
||||
|
||||
#include <asm/kvm_hyp.h>
|
||||
|
||||
size_t __ro_after_init kvm_hyp_nr_s2mpus;
|
||||
struct s2mpu __ro_after_init *kvm_hyp_s2mpus;
|
||||
|
||||
const struct kvm_iommu_ops kvm_s2mpu_ops = (struct kvm_iommu_ops){};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/kvm_mmu.h>
|
||||
@@ -22,6 +23,14 @@ struct s2mpu_irq_info {
|
||||
void __iomem *va;
|
||||
};
|
||||
|
||||
struct s2mpu_list_entry {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
struct s2mpu info;
|
||||
};
|
||||
|
||||
static LIST_HEAD(s2mpu_list);
|
||||
|
||||
static irqreturn_t s2mpu_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct s2mpu_irq_info *info = data;
|
||||
@@ -109,15 +118,16 @@ static u32 gen_ctx_cfg_valid_vid(struct platform_device *pdev,
|
||||
| CTX_CFG_ENTRY(7, ctx, ctx_vid[7]);
|
||||
}
|
||||
|
||||
static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr)
|
||||
static int s2mpu_probe_v9(struct platform_device *pdev, void __iomem *kaddr,
|
||||
struct s2mpu *info)
|
||||
{
|
||||
unsigned int num_ctx;
|
||||
u32 ssmt_valid_vid_bmap, ctx_cfg_valid_vid;
|
||||
u32 ssmt_valid_vid_bmap;
|
||||
|
||||
ssmt_valid_vid_bmap = ALL_VIDS_BITMAP;
|
||||
num_ctx = readl_relaxed(kaddr + REG_NS_NUM_CONTEXT) & NUM_CONTEXT_MASK;
|
||||
ctx_cfg_valid_vid = gen_ctx_cfg_valid_vid(pdev, num_ctx, ssmt_valid_vid_bmap);
|
||||
if (!ctx_cfg_valid_vid) {
|
||||
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;
|
||||
}
|
||||
@@ -168,10 +178,16 @@ static int s2mpu_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
void __iomem *kaddr;
|
||||
size_t res_size;
|
||||
enum s2mpu_power_state power_state = S2MPU_POWER_ALWAYS_ON;
|
||||
u32 version, power_domain_id = 0;
|
||||
struct s2mpu_list_entry *entry;
|
||||
struct s2mpu *info;
|
||||
int ret;
|
||||
|
||||
entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
entry->dev = &pdev->dev;
|
||||
info = &entry->info;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to parse 'reg'");
|
||||
@@ -191,6 +207,7 @@ static int s2mpu_probe(struct platform_device *pdev)
|
||||
res->start);
|
||||
return -EINVAL;
|
||||
}
|
||||
info->pa = res->start;
|
||||
|
||||
res_size = resource_size(res);
|
||||
if (res_size != S2MPU_MMIO_SIZE) {
|
||||
@@ -201,9 +218,9 @@ static int s2mpu_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "power-domain-id",
|
||||
&power_domain_id);
|
||||
&info->power_domain_id);
|
||||
if (!ret) {
|
||||
power_state = S2MPU_POWER_ON;
|
||||
info->power_state = S2MPU_POWER_ON;
|
||||
} else if (ret != -EINVAL) {
|
||||
dev_err(&pdev->dev, "failed to parse power-domain-id: %d", ret);
|
||||
return ret;
|
||||
@@ -216,20 +233,23 @@ static int s2mpu_probe(struct platform_device *pdev)
|
||||
*/
|
||||
s2mpu_probe_irq(pdev, kaddr);
|
||||
|
||||
version = readl_relaxed(kaddr + REG_NS_VERSION);
|
||||
switch (version & VERSION_CHECK_MASK) {
|
||||
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);
|
||||
ret = s2mpu_probe_v9(pdev, kaddr, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "unexpected version 0x%08x", version);
|
||||
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++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -245,16 +265,61 @@ static struct platform_driver of_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct s2mpu *alloc_s2mpu_array(void)
|
||||
{
|
||||
unsigned int order;
|
||||
|
||||
order = get_order(kvm_hyp_nr_s2mpus * sizeof(struct s2mpu));
|
||||
return (struct s2mpu *)__get_free_pages(GFP_KERNEL, order);
|
||||
}
|
||||
|
||||
static void free_s2mpu_array(struct s2mpu *array)
|
||||
{
|
||||
unsigned int order;
|
||||
|
||||
order = get_order(kvm_hyp_nr_s2mpus * sizeof(struct s2mpu));
|
||||
free_pages((unsigned long)array, order);
|
||||
}
|
||||
|
||||
static int create_s2mpu_array(struct s2mpu **array)
|
||||
{
|
||||
struct s2mpu_list_entry *entry, *tmp;
|
||||
size_t i;
|
||||
|
||||
*array = alloc_s2mpu_array();
|
||||
if (!*array)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Copy list to hyp array and destroy the list in the process. */
|
||||
i = 0;
|
||||
list_for_each_entry_safe(entry, tmp, &s2mpu_list, list) {
|
||||
(*array)[i++] = entry->info;
|
||||
list_del(&entry->list);
|
||||
devm_kfree(entry->dev, entry);
|
||||
}
|
||||
WARN_ON(i != kvm_hyp_nr_s2mpus);
|
||||
|
||||
kvm_hyp_s2mpus = kern_hyp_va(*array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_s2mpu_init(void)
|
||||
{
|
||||
struct s2mpu *s2mpus = NULL;
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_probe(&of_driver, s2mpu_probe);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = create_s2mpu_array(&s2mpus);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
kvm_info("S2MPU driver initialized\n");
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
free_s2mpu_array(s2mpus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user