arch/mips/mm/tlb-r4k.c

Source file repositories/reference/linux-study-clean/arch/mips/mm/tlb-r4k.c

File Facts

System
Linux kernel
Corpus path
arch/mips/mm/tlb-r4k.c
Extension
.c
Size
21459 bytes
Lines
863
Domain
Architecture Layer
Bucket
arch/mips
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct tlbent {
	unsigned long long wired:1;
	unsigned long long global:1;
	unsigned long long asid:10;
	unsigned long long vpn:51;
	unsigned long long pagesz:5;
	unsigned long long index:14;
};

/*
 * Comparison function for TLB entry sorting.  Place wired entries first,
 * then global entries, then order by the increasing VPN/ASID and the
 * decreasing page size.  This lets us avoid clashes with wired entries
 * easily and get entries for larger pages out of the way first.
 *
 * We could group bits so as to reduce the number of comparisons, but this
 * is seldom executed and not performance-critical, so prefer legibility.
 */
static int r4k_entry_cmp(const void *a, const void *b)
{
	struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b;

	if (ea.wired > eb.wired)
		return -1;
	else if (ea.wired < eb.wired)
		return 1;
	else if (ea.global > eb.global)
		return -1;
	else if (ea.global < eb.global)
		return 1;
	else if (ea.vpn < eb.vpn)
		return -1;
	else if (ea.vpn > eb.vpn)
		return 1;
	else if (ea.asid < eb.asid)
		return -1;
	else if (ea.asid > eb.asid)
		return 1;
	else if (ea.pagesz > eb.pagesz)
		return -1;
	else if (ea.pagesz < eb.pagesz)
		return 1;
	else
		return 0;
}

/*
 * Fetch all the TLB entries.  Mask individual VPN values retrieved with
 * the corresponding page mask and ignoring any 1KiB extension as we'll
 * be using 4KiB pages for uniquification.
 */
static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize)
{
	int start = num_wired_entries();
	unsigned long long vpn_mask;
	bool global;
	int i;

	vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT);
	vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31;

	for (i = 0; i < tlbsize; i++) {
		unsigned long long entryhi, vpn, mask, asid;
		unsigned int pagesz;

		write_c0_index(i);
		mtc0_tlbr_hazard();
		tlb_read();
		tlb_read_hazard();

		global = !!(read_c0_entrylo0() & ENTRYLO_G);
		entryhi = read_c0_entryhi_native();
		mask = read_c0_pagemask();

		asid = entryhi & cpu_asid_mask(&current_cpu_data);
		vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT;
		pagesz = ilog2((mask >> VPN2_SHIFT) + 1);

		tlb_vpns[i].global = global;
		tlb_vpns[i].asid = global ? 0 : asid;
		tlb_vpns[i].vpn = vpn;
		tlb_vpns[i].pagesz = pagesz;
		tlb_vpns[i].wired = i < start;
		tlb_vpns[i].index = i;
	}
}

/*
 * Write unique values to all but the wired TLB entries each, using
 * the 4KiB page size.  This size might not be supported with R6, but

Annotation

Implementation Notes