| #include <string.h> | |
| #include <errno.h> | |
| #include <realmode.h> | |
| #include <biosint.h> | |
| /** | |
| * @file BIOS interrupts | |
| * | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); | |
| /** | |
| * Hook INT vector | |
| * | |
| * @v interrupt INT number | |
| * @v handler Offset within .text16 to interrupt handler | |
| * @v chain_vector Vector for chaining to previous handler | |
| * | |
| * Hooks in an i386 INT handler. The handler itself must reside | |
| * within the .text16 segment. @c chain_vector will be filled in with | |
| * the address of the previously-installed handler for this interrupt; | |
| * the handler should probably exit by ljmping via this vector. | |
| */ | |
| void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler, | |
| struct segoff *chain_vector ) { | |
| struct segoff vector = { | |
| .segment = rm_cs, | |
| .offset = handler, | |
| }; | |
| DBG ( "Hooking INT %#02x to %04x:%04x\n", | |
| interrupt, rm_cs, handler ); | |
| if ( ( chain_vector->segment != 0 ) || | |
| ( chain_vector->offset != 0 ) ) { | |
| /* Already hooked; do nothing */ | |
| DBG ( "...already hooked\n" ); | |
| return; | |
| } | |
| copy_from_real ( chain_vector, 0, ( interrupt * 4 ), | |
| sizeof ( *chain_vector ) ); | |
| DBG ( "...chaining to %04x:%04x\n", | |
| chain_vector->segment, chain_vector->offset ); | |
| if ( DBG_LOG ) { | |
| char code[64]; | |
| copy_from_real ( code, chain_vector->segment, | |
| chain_vector->offset, sizeof ( code ) ); | |
| DBG_HDA ( *chain_vector, code, sizeof ( code ) ); | |
| } | |
| copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) ); | |
| hooked_bios_interrupts++; | |
| } | |
| /** | |
| * Unhook INT vector | |
| * | |
| * @v interrupt INT number | |
| * @v handler Offset within .text16 to interrupt handler | |
| * @v chain_vector Vector containing address of previous handler | |
| * | |
| * Unhooks an i386 interrupt handler hooked by hook_i386_vector(). | |
| * Note that this operation may fail, if some external code has hooked | |
| * the vector since we hooked in our handler. If it fails, it means | |
| * that it is not possible to unhook our handler, and we must leave it | |
| * (and its chaining vector) resident in memory. | |
| */ | |
| int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, | |
| struct segoff *chain_vector ) { | |
| struct segoff vector; | |
| DBG ( "Unhooking INT %#02x from %04x:%04x\n", | |
| interrupt, rm_cs, handler ); | |
| copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) ); | |
| if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) { | |
| DBG ( "...cannot unhook; vector points to %04x:%04x\n", | |
| vector.segment, vector.offset ); | |
| return -EBUSY; | |
| } | |
| DBG ( "...restoring to %04x:%04x\n", | |
| chain_vector->segment, chain_vector->offset ); | |
| copy_to_real ( 0, ( interrupt * 4 ), chain_vector, | |
| sizeof ( *chain_vector ) ); | |
| chain_vector->segment = 0; | |
| chain_vector->offset = 0; | |
| hooked_bios_interrupts--; | |
| return 0; | |
| } | |
| /** | |
| * Dump changes to interrupt vector table (for debugging) | |
| * | |
| */ | |
| void check_bios_interrupts ( void ) { | |
| static struct segoff vectors[256]; | |
| static uint8_t initialised; | |
| struct segoff vector; | |
| unsigned int i; | |
| /* Print any changed interrupt vectors */ | |
| for ( i = 0; i < ( sizeof ( vectors ) / sizeof ( vectors[0] ) ); i++ ) { | |
| copy_from_real ( &vector, 0, ( i * sizeof ( vector ) ), | |
| sizeof ( vector ) ); | |
| if ( memcmp ( &vector, &vectors[i], sizeof ( vector ) ) == 0 ) | |
| continue; | |
| if ( initialised ) { | |
| dbg_printf ( "INT %02x changed %04x:%04x => " | |
| "%04x:%04x\n", i, vectors[i].segment, | |
| vectors[i].offset, vector.segment, | |
| vector.offset ); | |
| } | |
| memcpy ( &vectors[i], &vector, sizeof ( vectors[i] ) ); | |
| } | |
| initialised = 1; | |
| } |