]> git.buserror.net Git - polintos/scott/priv.git/blob - kernel/arch/x64/misc.cc
8a364cb3df6152df7193b5a14f381685e78e74d1
[polintos/scott/priv.git] / kernel / arch / x64 / misc.cc
1 // arch/x64/misc.cc -- Misc. arch-specific stuff
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // This software is provided 'as-is', without any express or implied warranty.
6 // In no event will the authors or contributors be held liable for any damages
7 // arising from the use of this software.
8 // 
9 // Permission is hereby granted to everyone, free of charge, to use, copy,
10 // modify, prepare derivative works of, publish, distribute, perform,
11 // sublicense, and/or sell copies of the Software, provided that the above
12 // copyright notice and disclaimer of warranty be included in all copies or
13 // substantial portions of this software.
14
15 #include <kern/types.h>
16 #include <kern/libc.h>
17 #include <kern/arch.h>
18 #include <kern/i8259.h>
19 #include <kern/time.h>
20 #include <kern/thread.h>
21 #include <kern/mem.h>
22
23 #include <arch/addrs.h>
24 #include <arch/multiboot.h>
25
26 extern u64 x64_init_ptbl_l4[512];
27
28 struct X64InitStack {
29         u8 stack[4096 - ::Threads::thread_size];
30         ::Threads::Thread thread;
31 } __attribute__((aligned(4096))) x64_init_stack;
32
33 namespace Arch {
34         namespace Priv {
35                 void set_idt();
36                 
37                 void show_regs(u64 *stack) {
38                         for (int i = 0; i < 16; i += 4)
39                                 printf("r%02d 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
40                                        i, stack[i], stack[i + 1], stack[i + 2], stack[i + 3]);
41                         
42                         printf("orig rsp: 0x%016llx rflags: 0x%016llx\n",
43                                stack[19], stack[18]);
44                         
45                         printf("Thread %p (%s)\n", curthread, curthread->name);
46                         
47                         printf("Stack trace:       ");
48                         u64 *frame = (u64 *)stack[5];
49                         
50                         for (int i = 1; i < 32; i++) {
51                                 u64 stackptr = frame[1];
52                                 frame = (u64 *)frame[0];
53                                 
54                                 if ((u64)frame < 0xffff800000000000)
55                                         break;
56
57                                 if (!(i & 3))
58                                         printf("\n");
59                                 
60                                 printf("0x%016llx ", stackptr);
61                         }
62                 }
63
64                 struct TimerInt : public IRQ::Interrupt {
65                         bool action()
66                         {
67                                 Time::monotonic_timers->run();
68                                 return true;
69                         }
70                 };
71                 
72                 TimerInt timer_int;
73         }
74
75         using IRQ::i8259;
76         ::Threads::Thread *init_thread;
77                 
78         void arch_init()
79         {
80                 init_thread = &x64_init_stack.thread;
81                 Priv::early_adjust_mappings();
82                 Priv::set_idt();
83                 Priv::MultiBoot::process_info();
84                 i8259.init();
85                 
86                 u64 tss_addr = reinterpret_cast<u64>(&Priv::tss) + 4;
87                 x64_gdt[4].base_low = tss_addr & 0xffff;
88                 x64_gdt[4].base_mid = (tss_addr & 0xff0000) >> 16;
89                 x64_gdt[4].base_high = (tss_addr & 0xff000000) >> 24;
90                 
91                 asm volatile("ltr %w0" : : "r" (0x20) : "memory");
92                 init_thread->addr_space = new Mem::AddrSpace(x64_init_ptbl_l4);
93                 init_thread->active_addr_space = init_thread->addr_space;
94         }
95
96         void timer_init()
97         {
98                 IRQ::InterruptSlot *timer = i8259.get_slot(0);
99                 i8259.request_int(timer, &Priv::timer_int);
100         }
101         
102         void ArchThread::init(void *entry, void *arg)
103         {
104                 void **stack = reinterpret_cast<void **>(this);
105                 
106                 *--stack = arg;
107                 *--stack = entry;
108                 
109                 rsp = stack;
110                 rbp = 0;
111                 jump_to_init = 1;
112         }
113 }
114
115 using Arch::Priv::show_regs;
116
117 extern "C" void x64_do_diverr(u64 *stack)
118 {
119         printf("Division error at 0x%04llx:0x%016llx\n", stack[17], stack[16]);
120         show_regs(stack);
121         for(;;);
122 }
123
124 extern "C" void x64_do_invalid_insn(u64 *stack)
125 {
126         printf("Invalid instruction at 0x%04llx:0x%016llx\n", stack[17], stack[16]);
127         show_regs(stack);
128         for(;;);
129 }
130
131 extern "C" void x64_do_page_fault(u64 *stack, u64 fault_addr, u32 error_code)
132 {
133         Mem::AddrSpace *as;
134         
135         if (in_fault)
136                 for(;;);
137                 
138         // A reserved bit was set in the PTE; this is always a bug.
139         if (error_code & 8)
140                 goto bad_fault;
141         
142         // Don't try to fix up a page fault if interrupts were disabled.  It is an
143         // error to access non-locked pages with interrupts disabled.  Trying to
144         // fix it up in the case of an access that would be legitimate if interrupts
145         // were enabled would simply mask the loss of atomicity, and trying to grab
146         // locks to look up the address if it is a completely bad reference won't
147         // accomplish much other than decreasing the odds that the fault message
148         // gets out.
149         
150         if (!(stack[18] & 0x200))
151                 goto bad_fault;
152         
153         // Don't allow fault-ins using a borrowed addr-space.
154         as = curthread->addr_space;
155
156         if (!as || curthread == Arch::init_thread)
157                 goto bad_fault;
158         
159         ll_ints_on();
160         
161         // FIXME: no-exec
162         if (as->handle_fault(fault_addr, error_code & 2,
163                              false /* error_code & 16 */, error_code & 4))
164                 return;
165
166         // FIXME: throw exception to user
167
168 bad_fault:
169         ll_ints_off();
170         in_fault++;
171         
172         printf("Page fault at 0x%04llx:0x%016llx for 0x%016llx\n",
173                stack[17], stack[16], fault_addr);
174         printf("Error code: 0x%04x\n", error_code);
175         
176         show_regs(stack);
177
178         for(;;);
179 }
180
181 extern "C" void x64_do_gpf(u64 *stack, u32 error_code)
182 {
183         if (in_fault)
184                 for(;;);
185         
186         in_fault++;
187
188         printf("General protection fault at 0x%04llx:0x%016llx, "
189                "Error code: 0x%04x\n",
190                stack[17], stack[16], error_code);
191
192         show_regs(stack);
193         
194         for(;;);
195 }
196
197 extern "C" void x64_do_irq(int irq)
198 {
199         IRQ::i8259.handle_irq(irq - 0x20);
200 }