Allocating read/write storage

Updated: October 26, 2022

In some cases, kernel callouts need access to static read/write storage, in particular to share information with other kernel callouts.

Because kernel callout code is position-independent (see Common characteristics), it cannot have static read/write storage. However, you can assign a small amount a memory at the end of the system page to a kernel callout (or multiple kernel callouts) to use as read/write storage.

You can use the CALLOUT_START macro's second argument to specify the address of a four-byte variable that contains the amount of read/write storage (in bytes) the callout needs. For example, in the code snippet below:

  1. rw_interrupt has the address of the location with the number of bytes of storage required.
  2. Since the location referenced by rw_interrupt contains the value 4, the startup library allocates four bytes at the end of the system page.
  3. The library uses the patcher routine's rw_offset argument to pass the offset of the allocated memory (see Patcher routines) to this routine.
  4. The patcher routine modifies the initial instruction of the callout to refer to the appropriate offset, so that while the callout is executing, the t3 register contains a pointer to the read/write storage.
Here is the code snippet:
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_my_board, rw_interrupt, patch_interrupt)
/*
 * Input Parameters : 
 *  	a0 - syspage_ptr
 * 		a1 - Interrupt Number
 * Returns:
 *		v0 - error status
 */
		
	...
CALLOUT_END(interrupt_mask_my_board)

Sharing data between kernel callouts

If you set aside read/write static storage at the end of the system page for one kernel callout, you can use this area to pass data between callouts. The startup library passes the same rw_offset value to the patchers for all callouts that share the same address in their CALLOUT_START macro's second argument.

For example, because the patchers for the following callouts have the same rw_offset argument value passed to patch_interrupt(), they share the same read/write storage, which they can use to exchange data as needed:

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

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

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

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