Getting some R/W storage

Your callouts may need to have access to some static read/write storage. Normally this wouldn't be possible because of the position-independent requirements of a callout. But you can do it by using the patcher routines and the second parameter to CALLOUT_START. The second parameter to CALLOUT_START is the address of a four-byte variable that contains the amount of read/write storage the callout needs. For example:

rw_interrupt:
	.long	4
	
patch_interrupt:
	add		a1,a1,a2
	j	ra
	sh		a3,0+LOW16(a1)
	
/*
 * Mask the specified interrupt
 */
CALLOUT_START(interrupt_mask_mips, rw_interrupt, patch_interrupt)
/*
	 * Input Parameters : 
	 *      a0 - syspage_ptr
	 *      a1 - Interrupt Number
	 * Returns:
	 *		v0 - error status
	 */
		
	/*
	 * Mark the interrupt disabled 
	 */
	la		 t3,0x1234(a0)			# get enabled levels addr (patched)
	li       t1, MIPS_SREG_IMASK0		
		....
CALLOUT_END(interrupt_mask_mips)

The rw_interrupt address as the second parameter tells the startup library that the routine needs four bytes of read/write storage (since the contents at that location is a 4). The startup library allocates space at the end of the system page and passes the offset to it as the rw_offset parameter of the patcher routine. The patcher routine then modifies the initial instruction of the callout to the appropriate offset. While the callout is executing, the t3 register will contain a pointer to the read/write storage. The question you're undoubtedly asking at this point is: Why is the CALLOUT_START parameter the address of a location containing the amount of storage? Why not just pass the amount of storage directly?

That's a fair question. It's all part of a clever plan. A group of related callouts may want to have access to shared storage so that they can pass information among themselves. The library passes the same rw_offset value to the patcher routine for all routines that share the same address as the second parameter to CALLOUT_START. In other words:

CALLOUT_START(interrupt_mask_mips, rw_interrupt, patch_interrupt)
	....
CALLOUT_END(interrupt_mask_mips)

CALLOUT_START(interrupt_unmask_mips, rw_interrupt, patch_interrupt)
	....
CALLOUT_END(interrupt_unmask_mips)

CALLOUT_START(interrupt_eoi_mips, rw_interrupt, patch_interrupt)
	....
CALLOUT_END(interrupt_eoi_mips)

CALLOUT_START(interrupt_id_mips, rw_interrupt, patch_interrupt)
	....
CALLOUT_END(interrupt_id_mips)

will all get the same rw_offset parameter value passed to patch_interrupt() and thus will share the same read/write storage.