syspage(3): callouts

In order for the Neutrino microkernel to work on all boards, all hardware-dependent operations have been factored out of the code — Known as kernel callouts.

Callouts:

  • are provided by the startup program.
  • get overwritten when the kernel starts up.
    • Startup program will copy the callouts (the code between CALLOUT_START and CALLOUT_END)from the startup program into the system page and after this, the startup memory (text and data) is freed.
  • allow you to “hook into” the kernel and gain control when a given event occurs.
    • The callouts operate in an environment similar to that of an interrupt service routine — you have a very limited stack, and you can’t invoke any kernel calls (such as mutex operations, etc.).
  • must be Position-independent
    • reason: they won’t be in the location that they were loaded in, they must be coded to be position-independent.
    • how: be coded in assembler
  • No static read/write storage
    • if needed, you can make a small storage available for it, by using the patcher routines and the 2nd parameter to CALLOUT_START. see the example below on how it works.
  • For all but two of teh routines (interrupt_id(), interrupt_eoi), the kernel invokes the callouts with the normal function-calling conventions
    • For performance reasons, the kernel intermixes id() and eoi() directly with kernel code.

Types of Callouts

  • debug interface
  • clock/timer interface
  • interrupt controller interface
    • 3 callouts for interrupt controller interface: mask(), unmask(), config().
    • 2 callouts as code stubs: id(), eoi()
    • Each group of callouts (i.e. id, eoi, mask, unmask) for each level of interrupt controller deals with a set of interrupt vectors that start at 0 (zero-based).
  • cache controller interface
  • system reset
  • power management

Callout macros, defined in “callout.ah”

rw_intr: .word 8

CALLOUT_START(interrupt_id_gic, rw_intr, patch_id)

  •   1st parameter: name of the callout routine
  • 2nd parameter: address of a 4-byte variable that contains the amount of  read/write storage the callout needs.
  • 3rd parameter: either a zero or the address of a patcher() routine.

CALLOUT_END(interrupt_id_gic)

“patching” the callout code

  • make it possible for the same callout routine to be used on the different boards, where the device might be at different locations.
  • a patcher() is invoked immediately after the callout has been copied to its final resting place.
  • make read-write storage available for the callout, when used together with 2nd parameter of CALLOUT_START.
    • “rw_intr: .word 8″ tells the startup library that the routine needs 8 bytes of read/write storage.
    • 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.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s