e1a32bbb3d4a03eed7af0bc4f155d03bce6e9784
[polintos/scott/priv.git] / kernel / arch / x86 / descriptors.cc
1 // arch/x86/descriptors.cc -- code to manage segment/trap/interrupt descriptors
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following conditions:
11 // 
12 //     * Redistributions of source code must retain the above copyright notice,
13 //       this list of conditions and the following disclaimers.
14 // 
15 //     * Redistributions in binary form must reproduce the above copyright notice,
16 //       this list of conditions and the following disclaimers in the
17 //       documentation and/or other materials provided with the distribution.
18 // 
19 //     * The names of the Software's authors and/or contributors
20 //       may not be used to endorse or promote products derived from
21 //       this Software without specific prior written permission.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
25 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
29 // SOFTWARE.
30
31 #include <kern/types.h>
32 #include <arch/addrs.h>
33 #include <arch/thread.h>
34 #include <arch/mem.h>
35
36 namespace Arch {
37 namespace Priv {
38         TSS tss;
39 }
40 }
41
42 using Arch::Priv::Descriptor;
43 using Arch::Priv::TSS;
44 using Arch::Priv::tss;
45
46 Descriptor x86_gdt[1024] = {
47         {}, // The first entry is reserved for the NULL selector.
48         {   // 0x08: data
49                 limit_low: 0xffff,
50                 base_low: 0,
51                 base_mid: 0,
52                 type: 2, // data segment, writable
53                 user: 1,
54                 dpl: 0,
55                 present: 1,
56                 limit_high: 0xff,
57                 sw: 0,
58                 reserved: 0,
59                 opsize: 1,
60                 gran: 1,
61                 base_high: 0
62         },
63         {   // 0x10: code
64                 limit_low: 0xffff,
65                 base_low: 0,
66                 base_mid: 0,
67                 type: 10, // code segment, readable
68                 user: 1,
69                 dpl: 0,
70                 present: 1,
71                 limit_high: 0xff,
72                 sw: 0,
73                 reserved: 0,
74                 opsize: 1,
75                 gran: 1,
76                 base_high: 0
77         },
78         {   // 0x18: TSS
79                 limit_low: sizeof(tss),
80                 base_low: 0,
81                 base_mid: 0,
82                 type: 9,
83                 user: 0,
84                 dpl: 0,
85                 present: 1,
86                 limit_high: 0,
87                 sw: 0,
88                 reserved: 0,
89                 opsize: 0,
90                 gran: 0,
91                 base_high: 0
92         }
93 };
94
95 struct X86DescriptorTablePointer {
96         u8 pad[6];
97         u16 limit;
98         u32 address;
99 };
100
101 X86DescriptorTablePointer x86_gdtr = {
102         // G++ still won't handle complex labelled initializers, so
103         // we have to explicitly initialize pad.
104         
105         pad: { 0, 0, 0, 0, 0, 0 }, 
106         limit: sizeof(x86_gdt),
107         address: reinterpret_cast<u32>(&x86_gdt)
108 };
109
110 X86DescriptorTablePointer x86_gdtr_phys = {
111         pad: { 0, 0, 0, 0, 0, 0 }, 
112         limit: sizeof(x86_gdt),
113         address: reinterpret_cast<u32>(&x86_gdt) - KERNEL_START
114 };
115
116 struct X86InterruptDescriptor {
117         u16 offset_low;
118         u16 selector;
119         u8 stack_index:3;
120         u8 reserved:5;
121         u8 type:4;
122         u8 user:1;
123         u8 dpl:2;
124         u8 present:1;
125         u16 offset_high;
126 };
127
128 static X86InterruptDescriptor idt[256];
129
130 static X86DescriptorTablePointer idtr = {
131         pad: { 0, 0, 0, 0, 0, 0 }, 
132         limit: sizeof(idt),
133         address: reinterpret_cast<u32>(&idt)
134 };
135
136 // Set a gate for INT num to start executing at addr.  
137 //
138 // If ints_off is set, then interrupts will remain disabled until
139 // software enables them; otherwise, the interrupt flag will have the
140 // same state as in the code that was interrupted.
141 //
142 // If user is true, then this gate can be called directly from
143 // userspace with the INT instruction.  Otherwise, usermode
144 // INT to this gate will cause a GPF, but it may still be reached
145 // from user code via hardware interrupt or exception.
146 // 
147 // If stack_index is non-zero, use the specified alternate stack even if
148 // interrupting kernel code.
149
150 static void set_int_gate(int num, void *addrptr, bool ints_off = false,
151                          bool user = false, int stack_index = 0)
152 {
153         u32 addr = (u32)addrptr;
154
155         X86InterruptDescriptor desc = {
156                 offset_low: addr & 0xffff,
157                 selector: 0x10,
158                 stack_index: stack_index,
159                 reserved: 0,
160                 type: ints_off ? 14 : 15,
161                 user: 0,
162                 dpl: user ? 3 : 0,
163                 present: 1,
164                 offset_high: (addr >> 16) & 0xffff,
165         };
166         
167         idt[num] = desc;
168 }
169
170 extern int x86_diverr, x86_debug, x86_breakpoint;
171 extern int x86_gpf, x86_page_fault, x86_invalid_insn, x86_int99_entry;
172 extern void *x86_irqs[256];
173
174 namespace Arch {
175 namespace Priv {
176         void set_idt()
177         {
178                 // GCC 4.0 pukes on "m" (&idtr.limit), saying it's not
179                 // directly addressable.
180                 
181                 asm volatile("lidtl 6(%0)" : : "r" (&idtr) : "memory");
182                 
183                 set_int_gate(0, &x86_diverr);
184                 set_int_gate(1, &x86_debug);
185                 set_int_gate(3, &x86_breakpoint);
186                 set_int_gate(6, &x86_invalid_insn);
187                 set_int_gate(13, &x86_gpf);
188                 set_int_gate(14, &x86_page_fault, true);
189                 set_int_gate(0x99, &x86_int99_entry);
190                 
191                 for (int i = 0x20; i < 0x30; i++)
192                         set_int_gate(i, x86_irqs[i], true);
193         }
194 }
195 }