static int allocate_vpe_l1_table()

in irq-gic-v3-its.c [2800:2921]


static int allocate_vpe_l1_table(void)
{
	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
	u64 val, gpsz, npg, pa;
	unsigned int psz = SZ_64K;
	unsigned int np, epp, esz;
	struct page *page;

	if (!gic_rdists->has_rvpeid)
		return 0;

	/*
	 * if VPENDBASER.Valid is set, disable any previously programmed
	 * VPE by setting PendingLast while clearing Valid. This has the
	 * effect of making sure no doorbell will be generated and we can
	 * then safely clear VPROPBASER.Valid.
	 */
	if (gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER) & GICR_VPENDBASER_Valid)
		gicr_write_vpendbaser(GICR_VPENDBASER_PendingLast,
				      vlpi_base + GICR_VPENDBASER);

	/*
	 * If we can inherit the configuration from another RD, let's do
	 * so. Otherwise, we have to go through the allocation process. We
	 * assume that all RDs have the exact same requirements, as
	 * nothing will work otherwise.
	 */
	val = inherit_vpe_l1_table_from_rd(&gic_data_rdist()->vpe_table_mask);
	if (val & GICR_VPROPBASER_4_1_VALID)
		goto out;

	gic_data_rdist()->vpe_table_mask = kzalloc(sizeof(cpumask_t), GFP_ATOMIC);
	if (!gic_data_rdist()->vpe_table_mask)
		return -ENOMEM;

	val = inherit_vpe_l1_table_from_its();
	if (val & GICR_VPROPBASER_4_1_VALID)
		goto out;

	/* First probe the page size */
	val = FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, GIC_PAGE_SIZE_64K);
	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
	val = gicr_read_vpropbaser(vlpi_base + GICR_VPROPBASER);
	gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val);
	esz = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val);

	switch (gpsz) {
	default:
		gpsz = GIC_PAGE_SIZE_4K;
		fallthrough;
	case GIC_PAGE_SIZE_4K:
		psz = SZ_4K;
		break;
	case GIC_PAGE_SIZE_16K:
		psz = SZ_16K;
		break;
	case GIC_PAGE_SIZE_64K:
		psz = SZ_64K;
		break;
	}

	/*
	 * Start populating the register from scratch, including RO fields
	 * (which we want to print in debug cases...)
	 */
	val = 0;
	val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, gpsz);
	val |= FIELD_PREP(GICR_VPROPBASER_4_1_ENTRY_SIZE, esz);

	/* How many entries per GIC page? */
	esz++;
	epp = psz / (esz * SZ_8);

	/*
	 * If we need more than just a single L1 page, flag the table
	 * as indirect and compute the number of required L1 pages.
	 */
	if (epp < ITS_MAX_VPEID) {
		int nl2;

		val |= GICR_VPROPBASER_4_1_INDIRECT;

		/* Number of L2 pages required to cover the VPEID space */
		nl2 = DIV_ROUND_UP(ITS_MAX_VPEID, epp);

		/* Number of L1 pages to point to the L2 pages */
		npg = DIV_ROUND_UP(nl2 * SZ_8, psz);
	} else {
		npg = 1;
	}

	val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, npg - 1);

	/* Right, that's the number of CPU pages we need for L1 */
	np = DIV_ROUND_UP(npg * psz, PAGE_SIZE);

	pr_debug("np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\n",
		 np, npg, psz, epp, esz);
	page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE));
	if (!page)
		return -ENOMEM;

	gic_data_rdist()->vpe_l1_base = page_address(page);
	pa = virt_to_phys(page_address(page));
	WARN_ON(!IS_ALIGNED(pa, psz));

	val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, pa >> 12);
	val |= GICR_VPROPBASER_RaWb;
	val |= GICR_VPROPBASER_InnerShareable;
	val |= GICR_VPROPBASER_4_1_Z;
	val |= GICR_VPROPBASER_4_1_VALID;

out:
	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
	cpumask_set_cpu(smp_processor_id(), gic_data_rdist()->vpe_table_mask);

	pr_debug("CPU%d: VPROPBASER = %llx %*pbl\n",
		 smp_processor_id(), val,
		 cpumask_pr_args(gic_data_rdist()->vpe_table_mask));

	return 0;
}