Cache control

The cache controller circuitry in your system determines whether you need to provide a callout for the kernel to interface to the cache controller.

On the x86 architecture, the cache controller is integrated tightly with the CPU, meaning that the kernel doesn't have to talk to the cache controller. On other architectures, such as ARM, you need a cache control kernel callout that can tell the cache controllers to invalidate portions of the cache when certain functions are performed in the kernel.

The kernel callout prototype for cache control is control(). This callout gets passed:

The kernel callout is responsible for returning the number of cache lines that it affected. The caller (the kernel) can use this information and call the control() kernel callout repeatedly at a higher level. A return of 0 (zero) indicates that the entire cache was affected (e.g., all cache entries were invalidated).

For more information about caches, see cacheattr in the System Page chapter.

Note: It is unlikely that you will need to write your own cache control kernel callout. In most cases, a cache callout from the standard startup library will work.

Flags

The kernel uses a control() function's flags argument to pass instructions into a cache control kernel callout. The values for these flags are the same as the MS_* flags used for the msync() function (e.g., MS_ASYNC: set-up controller to perform asynchronous writes, MS_INVALIDATE_ICACHE: invalidate instruction cache only).

Examples

Below are some kernel callouts that perform cache operations:

/*
 * Cortex A8 specific cache operations
 *
 * unsigned control(paddr32_t base,
 *		unsigned num_lines,
 *		int flags,
 *		struct cacheattr_entry *cache,
 *		volatile struct syspage_entry * )
 */

#include "callout.ah"

#define	MAX_LINES	32
#define	LINE_SIZE	64
#define LINE_LIMIT	1024

CALLOUT_START(cache_a8_i, 0, 0)
	/*
	 * For large flushes just do the whole cache
	 */
	cmp		r1, #LINE_LIMIT
	movhi	r0, #0
	mcrhi	p15, 0, r0, c7, c5, 0		// ICIALLU
	mcrhi	p15, 0, r1, c7, c10, 4		// DSB (deprecated encoding)
	movhi	pc, lr

	/*
	 * Trim the address to a cache line boundary, and stop at 32 lines
	 * to avoid having to re-issue the whole flush if we get preempted
	 */
	bic		r3, r0, #(LINE_SIZE-1)
	cmp		r1, #MAX_LINES
	movhi	r1, #MAX_LINES
	mov		r0, r1

	/*
	 * Invalidate lines by address
	 */
0:	mcr		p15, 0, r3, c7, c5, 1		// ICIMVAU
	add		r3, r3, #LINE_SIZE
	subs	r1, r1, #1
	bne		0b
	mcr		p15, 0, r1, c7, c5, 6		// BPIALL
	mcr		p15, 0, r1, c7, c10, 4		// DSB (deprecated encoding)
	mov	pc, lr
CALLOUT_END(cache_a8_i)

CALLOUT_START(cache_a8_d, 0, 0)
	/*
	 * Trim the address to a cache line boundary, and stop at 32 lines
	 * to avoid having to re-issue the whole flush if we get preempted
	 */
	bic		r3, r0, #(LINE_SIZE-1)
	cmp		r1, #MAX_LINES
	movhi	r1, #MAX_LINES
	mov		r0, r1

	tst		r2, #MS_INVALIDATE
	bne		1f

	/*
	 * Clean lines by address
	 */
0:	mcr		p15, 0, r3, c7, c10, 1		// DCCMVAC
	add		r3, r3, #LINE_SIZE
	subs	r1, r1, #1
	bne		0b
	mcr		p15, 0, r1, c7, c10, 4		// DSB (deprecated encoding)
	mov		pc, lr

	/*
	 * Clean and invalidate lines by address
	 */
1:	mcr		p15, 0, r3, c7, c14, 1		// DCCIMVAC
	add		r3, r3, #LINE_SIZE
	subs	r1, r1, #1
	bne		1b
	mcr		p15, 0, r1, c7, c10, 4		// DSB (deprecated encoding)
	mov		pc, lr
CALLOUT_END(cache_a8_d)