Switch to a simple X11-style license.
[polintos/scott/priv.git] / kernel / arch / x64 / descriptors.cc
1 // arch/x64/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 condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23 #include <kern/types.h>
24 #include <arch/addrs.h>
25 #include <arch/thread.h>
26 #include <arch/mem.h>
27
28 namespace Arch {
29 namespace Priv {
30         TSS tss;
31 }
32 }
33
34 using Arch::Priv::Descriptor;
35 using Arch::Priv::TSS;
36 using Arch::Priv::tss;
37
38 Descriptor x64_gdt[1024] = {
39         {}, // The first entry is reserved for the NULL selector.
40         {   // 0x08: data
41                 limit_low: 0xffff,
42                 base_low: 0,
43                 base_mid: 0,
44                 type: 2, // data segment, writable
45                 user: 1,
46                 dpl: 0,
47                 present: 1,
48                 limit_high: 0xff,
49                 sw: 0,
50                 code64: 0,
51                 opsize: 1,
52                 gran: 1,
53                 base_high: 0
54         },
55         {   // 0x10: 32-bit code
56                 limit_low: 0xffff,
57                 base_low: 0,
58                 base_mid: 0,
59                 type: 10, // code segment, readable
60                 user: 1,
61                 dpl: 0,
62                 present: 1,
63                 limit_high: 0xff,
64                 sw: 0,
65                 code64: 0,
66                 opsize: 1,
67                 gran: 1,
68                 base_high: 0
69         },
70         {   // 0x18: 64-bit code
71                 limit_low: 0xffff,
72                 base_low: 0,
73                 base_mid: 0,
74                 type: 10, // code segment, readable
75                 user: 1,
76                 dpl: 0,
77                 present: 1,
78                 limit_high: 0xff,
79                 sw: 0,
80                 code64: 1,
81                 opsize: 0,
82                 gran: 1,
83                 base_high: 0
84         },
85         {   // 0x20: TSS
86                 limit_low: sizeof(TSS) - 4,
87                 base_low: 0,
88                 base_mid: 0,
89                 type: 9,
90                 user: 0,
91                 dpl: 0,
92                 present: 1,
93                 limit_high: 0,
94                 sw: 0,
95                 code64: 0,
96                 opsize: 0,
97                 gran: 0,
98                 base_high: 0,
99         },
100         {   // 0x28: TSS high
101         }
102 };
103
104 struct X64DescriptorTablePointer {
105         u8 pad[6];
106         u16 limit;
107         u64 address;
108 };
109
110 X64DescriptorTablePointer x64_gdtr = {
111         // G++ still won't handle complex labelled initializers, so
112         // we have to explicitly initialize pad.
113         
114         pad: { 0, 0, 0, 0, 0, 0 }, 
115         limit: sizeof(x64_gdt),
116         address: reinterpret_cast<u64>(&x64_gdt)
117 };
118
119 X64DescriptorTablePointer x64_gdtr_phys = {
120         pad: { 0, 0, 0, 0, 0, 0 }, 
121         limit: sizeof(x64_gdt),
122         address: reinterpret_cast<u64>(&x64_gdt) - KERNEL_START
123 };
124
125 struct X64InterruptDescriptor {
126         u16 offset_low;
127         u16 selector;
128         u8 stack_index:3;
129         u8 reserved:5;
130         u8 type:4;
131         u8 user:1;
132         u8 dpl:2;
133         u8 present:1;
134         u16 offset_mid;
135         u32 offset_high;
136         u32 reserved2;
137 };
138         
139 static X64InterruptDescriptor idt[256];
140
141 static X64DescriptorTablePointer idtr = {
142         pad: { 0, 0, 0, 0, 0, 0 }, 
143         limit: sizeof(idt),
144         address: reinterpret_cast<u64>(&idt)
145 };
146
147 // Set a gate for INT num to start executing at addr.  
148 //
149 // If ints_off is set, then interrupts will remain disabled until
150 // software enables them; otherwise, the interrupt flag will have the
151 // same state as in the code that was interrupted.
152 //
153 // If user is true, then this gate can be called directly from
154 // userspace with the INT instruction.  Otherwise, usermode
155 // INT to this gate will cause a GPF, but it may still be reached
156 // from user code via hardware interrupt or exception.
157 // 
158 // If stack_index is non-zero, use the specified alternate stack even if
159 // interrupting kernel code.
160
161 static void set_int_gate(int num, void *addrptr, bool ints_off = false,
162                          bool user = false, int stack_index = 0)
163 {
164         u64 addr = (u64)addrptr;
165
166         X64InterruptDescriptor desc = {
167                 offset_low: addr & 0xffff,
168                 selector: 0x18,
169                 stack_index: stack_index,
170                 reserved: 0,
171                 type: ints_off ? 14 : 15,
172                 user: 0,
173                 dpl: user ? 3 : 0,
174                 present: 1,
175                 offset_mid: (addr >> 16) & 0xffff,
176                 offset_high: addr >> 32
177         };
178         
179         idt[num] = desc;
180 }
181
182 extern int x64_diverr, x64_gpf, x64_page_fault, x64_invalid_insn;
183 extern void *x64_irqs[256];
184
185 namespace Arch {
186 namespace Priv {
187         void set_idt()
188         {
189                 // GCC 4.0 pukes on "m" (&idtr.limit), saying it's not
190                 // directly addressable.
191                 
192                 asm volatile("lidtq 6(%0)" : : "r" (&idtr) : "memory");
193                 
194                 set_int_gate(0, &x64_diverr);
195                 set_int_gate(6, &x64_invalid_insn);
196                 set_int_gate(13, &x64_gpf);
197                 set_int_gate(14, &x64_page_fault, true);
198                 
199                 for (int i = 0x20; i < 0x30; i++)
200                         set_int_gate(i, x64_irqs[i]);
201         }
202 }
203 }