Dump first 64 bytes of chained ISR when debugging is enabled.
[people/holger/gpxe.git] / src / arch / i386 / interface / pcbios / biosint.c
1 #include <errno.h>
2 #include <realmode.h>
3 #include <biosint.h>
4
5 /**
6  * @file BIOS interrupts
7  *
8  */
9
10 /**
11  * Hooked interrupt count
12  *
13  * At exit, after unhooking all possible interrupts, this counter
14  * should be examined.  If it is non-zero, it means that we failed to
15  * unhook at least one interrupt vector, and so must not free up the
16  * memory we are using.  (Note that this also implies that we should
17  * re-hook INT 15 in order to hide ourselves from the memory map).
18  */
19 int hooked_bios_interrupts = 0;
20
21 /**
22  * Hook INT vector
23  *
24  * @v interrupt         INT number
25  * @v handler           Offset within .text16 to interrupt handler
26  * @v chain_vector      Vector for chaining to previous handler
27  *
28  * Hooks in an i386 INT handler.  The handler itself must reside
29  * within the .text16 segment.  @c chain_vector will be filled in with
30  * the address of the previously-installed handler for this interrupt;
31  * the handler should probably exit by ljmping via this vector.
32  */
33 void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
34                            struct segoff *chain_vector ) {
35         struct segoff vector = {
36                 .segment = rm_cs,
37                 .offset = handler,
38         };
39
40         DBG ( "Hooking INT %#02x to %04x:%04x\n",
41               interrupt, rm_cs, handler );
42
43         if ( ( chain_vector->segment != 0 ) ||
44              ( chain_vector->offset != 0 ) ) {
45                 /* Already hooked; do nothing */
46                 DBG ( "...already hooked\n" );
47                 return;
48         }
49
50         copy_from_real ( chain_vector, 0, ( interrupt * 4 ),
51                          sizeof ( *chain_vector ) );
52         DBG ( "...chaining to %04x:%04x\n",
53               chain_vector->segment, chain_vector->offset );
54         if ( DBG_LOG ) {
55                 char code[64];
56                 copy_from_real ( code, chain_vector->segment,
57                                  chain_vector->offset, sizeof ( code ) );
58                 DBG_HDA ( *chain_vector, code, sizeof ( code ) );
59         }
60
61         copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) );
62         hooked_bios_interrupts++;
63 }
64
65 /**
66  * Unhook INT vector
67  *
68  * @v interrupt         INT number
69  * @v handler           Offset within .text16 to interrupt handler
70  * @v chain_vector      Vector containing address of previous handler
71  *
72  * Unhooks an i386 interrupt handler hooked by hook_i386_vector().
73  * Note that this operation may fail, if some external code has hooked
74  * the vector since we hooked in our handler.  If it fails, it means
75  * that it is not possible to unhook our handler, and we must leave it
76  * (and its chaining vector) resident in memory.
77  */
78 int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler,
79                             struct segoff *chain_vector ) {
80         struct segoff vector;
81
82         DBG ( "Unhooking INT %#02x from %04x:%04x\n",
83               interrupt, rm_cs, handler );
84
85         copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) );
86         if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) {
87                 DBG ( "...cannot unhook; vector points to %04x:%04x\n",
88                       vector.segment, vector.offset );
89                 return -EBUSY;
90         }
91
92         DBG ( "...restoring to %04x:%04x\n",
93               chain_vector->segment, chain_vector->offset );
94         copy_to_real ( 0, ( interrupt * 4 ), chain_vector,
95                        sizeof ( *chain_vector ) );
96
97         chain_vector->segment = 0;
98         chain_vector->offset = 0;
99         hooked_bios_interrupts--;
100         return 0;
101 }