[GDB] Zero-extend 16-bit segment registers
[people/andreif/gpxe.git] / src / arch / i386 / core / gdbidt.S
1 /*
2  * Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub.
3  */
4
5 #include <virtaddr.h>
6
7 #define SIZEOF_I386_REGS        32
8 #define SIZEOF_I386_FLAGS       4
9
10 /****************************************************************************
11  * Interrupt Descriptor Table
12  ****************************************************************************
13  */
14         .section ".data16"
15         .globl idtr
16 idtr:
17 idt_limit:
18         .word   idt_length - 1
19 idt_base:
20         .long   0
21
22 /* IDT entries have the following format:
23  * offset_lo, segment selector, flags, offset_hi
24  *
25  * Since it is not possible to specify relocations in arbitrary
26  * expressions like (int_overflow & 0xffff), we initialise the
27  * IDT with entries in an incorrect format.
28  *
29  * The entries are shuffled into the correct format in init_librm().
30  */
31 #define IDT_ENTRY_EMPTY(name) .word 0, 0, 0, 0
32 #define IDT_ENTRY_PRESENT(name) \
33         .long   int_##name; \
34         .word   0x8e00, VIRTUAL_CS
35
36 .align 16
37 idt:
38         IDT_ENTRY_PRESENT(divide_error)
39         IDT_ENTRY_PRESENT(debug_trap)
40         IDT_ENTRY_EMPTY(non_maskable_interrupt)
41         IDT_ENTRY_PRESENT(breakpoint)
42         IDT_ENTRY_PRESENT(overflow)
43         IDT_ENTRY_PRESENT(bound_range_exceeded)
44         IDT_ENTRY_PRESENT(invalid_opcode)
45         IDT_ENTRY_EMPTY(device_not_available)
46         IDT_ENTRY_PRESENT(double_fault)
47         IDT_ENTRY_EMPTY(coprocessor_segment_overrun)
48         IDT_ENTRY_PRESENT(invalid_tss)
49         IDT_ENTRY_PRESENT(segment_not_present)
50         IDT_ENTRY_PRESENT(stack_segment_fault)
51         IDT_ENTRY_PRESENT(general_protection)
52         IDT_ENTRY_PRESENT(page_fault)
53 idt_end:
54         .equ    idt_length, idt_end - idt
55
56 /* The IDT entries are fixed up (once) in init_librm() */
57 idt_fixed:
58         .byte   0
59
60 /****************************************************************************
61  * idt_init (real-mode near call, 16-bit real-mode near return address)
62  *
63  * Initialise the IDT, called from init_librm.
64  *
65  * Parameters:
66  *   %eax : IDT base address
67  *
68  * Destroys %ax, %bx, and %di.
69  ****************************************************************************
70  */
71         .section ".text16"
72         .code16
73         .globl idt_init
74 idt_init:
75         movl    %eax, idt_base
76         addl    $idt, idt_base
77
78         /* IDT entries are only fixed up once */
79         movb    idt_fixed, %al
80         orb     %al, %al
81         jnz     2f
82         movb    $1, idt_fixed
83
84         /* Shuffle IDT entries into the correct format */
85         movb    $(idt_length / 8), %al
86         movw    $idt, %bx
87         or      %al, %al
88         jz      2f
89 1:
90         movw    2(%bx), %di
91         xchg    %di, 6(%bx)
92         movw    %di, 2(%bx)
93         addw    $8, %bx
94         dec     %al
95         jnz     1b
96 2:
97         ret
98
99 /****************************************************************************
100  * Interrupt handlers
101  ****************************************************************************
102  */
103         .section ".text"
104         .code32
105
106 /* POSIX signal numbers for reporting traps to GDB */
107 #define SIGILL 4
108 #define SIGTRAP 5
109 #define SIGBUS 7
110 #define SIGFPE 8
111 #define SIGSEGV 11
112 #define SIGSTKFLT 16
113
114 int_divide_error:
115         pushl   $SIGFPE
116         jmp     do_interrupt
117
118 int_debug_trap:
119 int_breakpoint:
120         pushl   $SIGTRAP
121         jmp     do_interrupt
122
123 int_overflow:
124 int_bound_range_exceeded:
125         pushl   $SIGSTKFLT
126         jmp     do_interrupt
127
128 int_invalid_opcode:
129         pushl   $SIGILL
130         jmp     do_interrupt
131
132 int_double_fault:
133         movl    $SIGBUS, (%esp)
134         jmp     do_interrupt
135
136 int_invalid_tss:
137 int_segment_not_present:
138 int_stack_segment_fault:
139 int_general_protection:
140 int_page_fault:
141         movl    $SIGSEGV, (%esp)
142         jmp     do_interrupt
143
144 /* When invoked, the stack contains: eflags, cs, eip, signo. */
145 #define IH_OFFSET_GDB_REGS ( 0 )
146 #define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS )
147 #define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 )
148 #define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS )
149 #define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 )
150 #define IH_OFFSET_SIGNO ( IH_OFFSET_GDB_END )
151 #define IH_OFFSET_OLD_EIP ( IH_OFFSET_SIGNO + 4 )
152 #define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 )
153 #define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 )
154 #define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 )
155
156 /* We also access the stack whilst still storing or restoring
157  * the register snapshot.  Since ESP is in flux, we need
158  * special offsets.
159  */
160 #define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 )
161 #define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 )
162 #define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 )
163 #define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 )
164 do_interrupt:
165         /* Store CPU state in GDB register snapshot */
166         pushw   $0
167         pushw   %gs
168         pushw   $0
169         pushw   %fs
170         pushw   $0
171         pushw   %es
172         pushw   $0
173         pushw   %ds
174         pushw   $0
175         pushw   %ss
176         pushw   $0
177         pushw   IH_OFFSET_FLUX_OLD_CS + 2(%esp)
178         pushl   IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
179         pushl   IH_OFFSET_FLUX_OLD_EIP(%esp)
180         pushl   %edi
181         pushl   %esi
182         pushl   %ebp
183         leal    IH_OFFSET_FLUX_END(%esp), %edi
184         pushl   %edi /* old ESP */
185         pushl   %ebx
186         pushl   %edx
187         pushl   %ecx
188         pushl   %eax
189
190         /* Call GDB stub exception handler */
191         pushl   %esp
192         pushl   (IH_OFFSET_SIGNO + 4)(%esp)
193         call    gdbmach_handler
194         addl    $8, %esp
195
196         /* Restore CPU state from GDB register snapshot */
197         popl    %eax
198         popl    %ecx
199         popl    %edx
200         popl    %ebx
201         addl    $4, %esp /* Changing ESP currently not supported */
202         popl    %ebp
203         popl    %esi
204         popl    %edi
205         popl    IH_OFFSET_FLUX_OLD_EIP(%esp)
206         popl    IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
207         popl    IH_OFFSET_FLUX_OLD_CS(%esp)
208         popl    %ss
209         popl    %ds
210         popl    %es
211         popl    %fs
212         popl    %gs
213
214         addl    $4, %esp /* drop signo */
215         iret