If a device for which you are writing a kernel callout can appear in different locations on different boards, a patch routine is required to add the addresses of the registers to the kernel callout code.
There are two reasons why kernel callouts may not know what addresses the registers occupy:
When it starts up, the procnto kernel and process manager runs any patchers that are present (see procnto* in the Utilities Reference). The third argument of the CALLOUT_START macro can be either 0 (nothing to do), or the address of a patcher routine.
Patcher routines have the following prototype:
void patcher( paddr_t paddr, paddr_t vaddr, unsigned rtn_offset, unsigned rw_offset, void *data, struct callout_rtn *src );
This routine is invoked immediately after the kernel callout has been copied to its final resting place. Its arguments are as follows:
Patcher routines don't have to be written in assembly. They are usually written in assembly, however, so that they can be kept in the same source file as the code that they patch.
If you arrange the first instructions in a group of related kernel callouts the same way (e.g. debug_char_*(), poll_key_*(), break_detect_*()), you can use the same patcher routine for these callouts.
Here's an example of a patcher routine for an x86 processor. We assume that the display_char_8250() routine has been copied to its permanent location in memory before we invoke our patcher.
The patch_debug_8250() modifies the constants in the first two instructions to the I/O port location and register spacing required for the board:
patch_debug_8250: movl 0x4(%esp),%eax // get paddr of routine addl 0xc(%esp),%eax // ... movl 0x14(%esp),%edx // get base info movl DDI_BASE(%edx),%ecx // patch code with real serial port movl %ecx,0x1(%eax) movl DDI_SHIFT(%edx),%ecx // patch code with register shift movl $REG_LS,%edx shll %cl,%edx movl %edx,0x6(%eax) ret CALLOUT_START(display_char_8250, 0, patch_debug_8250) movl $0x12345678,%edx // get serial port base (patched) movl $0x12345678,%ecx // get serial port shift (patched) .... CALLOUT_END(display_char_8250)