const struct pt_iommu_vtdss_cfg *cfg)
{
struct pt_vtdss *table = &iommu_table->vtdss_pt;
- unsigned int vasz_lg2 = cfg->common.hw_max_vasz_lg2;
- if (vasz_lg2 > PT_MAX_VA_ADDRESS_LG2)
- return -EOPNOTSUPP;
- else if (vasz_lg2 > 48)
- pt_top_set_level(&table->common, 4);
- else if (vasz_lg2 > 39)
- pt_top_set_level(&table->common, 3);
- else if (vasz_lg2 > 30)
- pt_top_set_level(&table->common, 2);
- else
+ if (cfg->top_level > 4 || cfg->top_level < 2)
return -EOPNOTSUPP;
+
+ pt_top_set_level(&table->common, cfg->top_level);
return 0;
}
#define pt_iommu_fmt_init vtdss_pt_iommu_fmt_init
#if defined(GENERIC_PT_KUNIT)
static const struct pt_iommu_vtdss_cfg vtdss_kunit_fmt_cfgs[] = {
- [0] = { .common.hw_max_vasz_lg2 = 39 },
- [1] = { .common.hw_max_vasz_lg2 = 48 },
- [2] = { .common.hw_max_vasz_lg2 = 57 },
+ [0] = { .common.hw_max_vasz_lg2 = 39, .top_level = 2},
+ [1] = { .common.hw_max_vasz_lg2 = 48, .top_level = 3},
+ [2] = { .common.hw_max_vasz_lg2 = 57, .top_level = 4},
};
#define kunit_fmt_cfgs vtdss_kunit_fmt_cfgs
enum { KUNIT_FMT_FEATURES = BIT(PT_FEAT_VTDSS_FORCE_WRITEABLE) };
PT_FORCE_ENABLED_FEATURES))
return -EOPNOTSUPP;
+ /*
+ * Check if the top level of the page table is too small to hold the
+ * specified maxvasz.
+ */
+ if (!pt_feature(common, PT_FEAT_DYNAMIC_TOP) &&
+ top_range.top_level != PT_MAX_TOP_LEVEL) {
+ struct pt_state pts = { .range = &top_range,
+ .level = top_range.top_level };
+
+ if (common->max_vasz_lg2 >
+ pt_num_items_lg2(&pts) + pt_table_item_lg2sz(&pts))
+ return -EOPNOTSUPP;
+ }
+
if (common->max_oasz_lg2 == 0)
common->max_oasz_lg2 = pt_max_oa_lg2(common);
else
return &dmar_domain->domain;
}
-static int compute_vasz_lg2_ss(struct intel_iommu *iommu)
+static unsigned int compute_vasz_lg2_ss(struct intel_iommu *iommu,
+ unsigned int *top_level)
{
unsigned int sagaw = cap_sagaw(iommu->cap);
unsigned int mgaw = cap_mgaw(iommu->cap);
/*
* Find the largest table size that both the mgaw and sagaw support.
- * This sets both the number of table levels and the valid range of
- * IOVA.
+ * This sets the valid range of IOVA and the top starting level.
+ * Some HW may only support a 4 or 5 level walk but must limit IOVA to
+ * 3 levels.
*/
- if (mgaw >= 48 && (sagaw & BIT(3)))
+ if (mgaw > 48 && sagaw >= BIT(3)) {
+ *top_level = 4;
return min(57, mgaw);
- else if (mgaw >= 39 && (sagaw & BIT(2)))
+ } else if (mgaw > 39 && sagaw >= BIT(2)) {
+ *top_level = 3 + ffs(sagaw >> 3);
return min(48, mgaw);
- else if (mgaw >= 30 && (sagaw & BIT(1)))
+ } else if (mgaw > 30 && sagaw >= BIT(1)) {
+ *top_level = 2 + ffs(sagaw >> 2);
return min(39, mgaw);
+ }
return 0;
}
if (IS_ERR(dmar_domain))
return ERR_CAST(dmar_domain);
- cfg.common.hw_max_vasz_lg2 = compute_vasz_lg2_ss(iommu);
+ cfg.common.hw_max_vasz_lg2 = compute_vasz_lg2_ss(iommu, &cfg.top_level);
cfg.common.hw_max_oasz_lg2 = 52;
cfg.common.features = BIT(PT_FEAT_FLUSH_RANGE);
struct pt_iommu_vtdss_cfg {
struct pt_iommu_cfg common;
+ /* 4 is a 57 bit 5 level table */
+ unsigned int top_level;
};
struct pt_iommu_vtdss_hw_info {