* Copyright 2013, Paweł Dziepak, pdziepak@quarnos.org.
* Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
* Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <arch/x86/descriptors.h>
#include <stdio.h>
#include <boot/kernel_args.h>
#include <cpu.h>
#include <interrupts.h>
#include <tls.h>
#include <vm/vm.h>
#include <vm/vm_priv.h>
#include <arch/int.h>
#include <arch/user_debugger.h>
#include "interrupts.h"
#define IDT_GATES_COUNT 256
typedef interrupt_descriptor interrupt_descriptor_table[IDT_GATES_COUNT];
global_descriptor_table gGDTs[SMP_MAX_CPUS];
static interrupt_descriptor_table sIDTs[SMP_MAX_CPUS];
typedef void interrupt_handler_function(struct iframe* frame);
static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT;
interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize];
*/
static void
set_gate(interrupt_descriptor *gate_addr, addr_t addr, int type, int dpl)
{
unsigned int gate1;
unsigned int gate2;
gate1 = (KERNEL_CODE_SELECTOR << 16) | (0x0000ffff & addr);
gate2 = (0xffff0000 & addr) | 0x8000 | (dpl << 13) | (type << 8);
gate_addr->a = gate1;
gate_addr->b = gate2;
}
specified CPU to an interrupt-gate descriptor with the given procedure
address.
For CPUs other than the boot CPU it must not be called before
arch_int_init_post_vm().
*/
static void
set_interrupt_gate(int32 cpu, int n, void (*addr)())
{
set_gate(&sIDTs[cpu][n], (addr_t)addr, 14, DPL_KERNEL);
}
specified CPU to an trap-gate descriptor with the given procedure address.
For CPUs other than the boot CPU it must not be called before
arch_int_init_post_vm().
*/
static void
set_trap_gate(int32 cpu, int n, void (*addr)())
{
set_gate(&sIDTs[cpu][n], (unsigned int)addr, 15, DPL_USER);
}
\a cpu to a task-gate descripter referring to the TSS segment identified
by TSS segment selector \a segment.
For CPUs other than the boot CPU it must not be called before
arch_int_init_post_vm() (arch_cpu_init_post_vm() is fine).
*/
static void
set_task_gate(int32 cpu, int32 n, int32 segment)
{
sIDTs[cpu][n].a = (segment << 16);
sIDTs[cpu][n].b = 0x8000 | (0 << 13) | (0x5 << 8);
}
static inline void
load_tss()
{
uint16 segment = (TSS_SEGMENT << 3) | DPL_KERNEL;
asm("ltr %w0" : : "r" (segment));
}
static inline void
load_gdt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED gdtDescriptor = {
GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1,
gGDTs[cpu]
};
asm volatile("lgdt %0" : : "m" (gdtDescriptor));
}
static inline void
load_idt(int cpu)
{
struct {
uint16 limit;
void* address;
} _PACKED idtDescriptor = {
IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1,
&sIDTs[cpu]
};
asm volatile("lidt %0" : : "m" (idtDescriptor));
}
void
x86_double_fault_exception(struct iframe* frame)
{
int cpu = x86_double_fault_get_cpu();
if (cpu < 0) {
kprintf("Double fault handler invoked, but can't determine CPU! "
"Entering infinite loop...\n");
while (true);
}
struct tss *tss = &gCPU[cpu].arch.tss;
frame->cs = tss->cs;
frame->es = tss->es;
frame->ds = tss->ds;
frame->fs = tss->fs;
frame->gs = tss->gs;
frame->ip = tss->eip;
frame->bp = tss->ebp;
frame->sp = tss->esp;
frame->ax = tss->eax;
frame->bx = tss->ebx;
frame->cx = tss->ecx;
frame->dx = tss->edx;
frame->si = tss->esi;
frame->di = tss->edi;
frame->flags = tss->eflags;
set_interrupt_gate(cpu, 14, &trap14_double_fault);
debug_double_fault(cpu);
}
void
x86_page_fault_exception_double_fault(struct iframe* frame)
{
addr_t cr2 = x86_read_cr2();
addr_t faultHandler = 0, faultHandlerStack;
int32 currentCPU = x86_double_fault_get_cpu();
if (currentCPU >= 0) {
cpu_ent& cpu = gCPU[currentCPU];
faultHandler = cpu.fault_handler;
faultHandlerStack = cpu.fault_handler_stack_pointer;
}
if (faultHandler != 0) {
debug_set_page_fault_info(cr2, frame->ip,
(frame->error_code & 0x2) != 0 ? DEBUG_PAGE_FAULT_WRITE : 0);
frame->ip = faultHandler;
frame->bp = faultHandlerStack;
return;
}
kprintf("Page fault in double fault debugger without fault handler! "
"Touching address %p from eip %p. Entering infinite loop...\n",
(void*)cr2, (void*)frame->ip);
while (true);
}
static void
init_double_fault(int cpuNum)
{
struct tss* tss = &gCPU[cpuNum].arch.double_fault_tss;
memset(tss, 0, sizeof(struct tss));
size_t stackSize;
tss->sp0 = (addr_t)x86_get_double_fault_stack(cpuNum, &stackSize);
tss->sp0 += stackSize;
tss->ss0 = KERNEL_DATA_SELECTOR;
tss->cr3 = x86_read_cr3();
tss->eip = (uint32)&double_fault;
tss->es = KERNEL_DATA_SELECTOR;
tss->cs = KERNEL_CODE_SELECTOR;
tss->ss = KERNEL_DATA_SELECTOR;
tss->esp = tss->sp0;
tss->ds = KERNEL_DATA_SELECTOR;
tss->fs = KERNEL_DATA_SELECTOR;
tss->gs = (KERNEL_TLS_SEGMENT << 3);
tss->ldt_seg_selector = 0;
tss->io_map_base = sizeof(struct tss);
set_tss_descriptor(&gGDTs[cpuNum][DOUBLE_FAULT_TSS_SEGMENT], (addr_t)tss,
sizeof(struct tss));
set_task_gate(cpuNum, 8, DOUBLE_FAULT_TSS_SEGMENT << 3);
}
static void
init_gdt_percpu(kernel_args* args, int cpu)
{
STATIC_ASSERT(GDT_SEGMENT_COUNT <= 8192);
segment_descriptor* gdt = get_gdt(cpu);
clear_segment_descriptor(&gdt[0]);
set_segment_descriptor(&gdt[KERNEL_CODE_SEGMENT], 0, addr_t(-1),
DT_CODE_READABLE, DPL_KERNEL);
set_segment_descriptor(&gdt[KERNEL_DATA_SEGMENT], 0, addr_t(-1),
DT_DATA_WRITEABLE, DPL_KERNEL);
set_segment_descriptor(&gdt[USER_CODE_SEGMENT], 0, addr_t(-1),
DT_CODE_READABLE, DPL_USER);
set_segment_descriptor(&gdt[USER_DATA_SEGMENT], 0, addr_t(-1),
DT_DATA_WRITEABLE, DPL_USER);
memset(&gCPU[cpu].arch.tss, 0, sizeof(struct tss));
gCPU[cpu].arch.tss.ss0 = (KERNEL_DATA_SEGMENT << 3) | DPL_KERNEL;
gCPU[cpu].arch.tss.io_map_base = sizeof(struct tss);
set_tss_descriptor(&gdt[TSS_SEGMENT], (addr_t)&gCPU[cpu].arch.tss,
sizeof(struct tss));
init_double_fault(cpu);
set_segment_descriptor(&gdt[KERNEL_TLS_SEGMENT],
(addr_t)&gCPU[cpu].arch.kernel_tls, sizeof(void*), DT_DATA_WRITEABLE,
DPL_KERNEL);
set_segment_descriptor(&gdt[USER_TLS_SEGMENT], 0, TLS_SIZE,
DT_DATA_WRITEABLE, DPL_USER);
load_gdt(cpu);
load_tss();
asm volatile("movw %w0, %%gs" : : "r" (KERNEL_TLS_SEGMENT << 3));
}
static void
init_idt_percpu(kernel_args* args, int cpu)
{
set_interrupt_gate(cpu, 0, &trap0);
set_interrupt_gate(cpu, 1, &trap1);
set_interrupt_gate(cpu, 2, &trap2);
set_trap_gate(cpu, 3, &trap3);
set_interrupt_gate(cpu, 4, &trap4);
set_interrupt_gate(cpu, 5, &trap5);
set_interrupt_gate(cpu, 6, &trap6);
set_interrupt_gate(cpu, 7, &trap7);
set_interrupt_gate(cpu, 9, &trap9);
set_interrupt_gate(cpu, 10, &trap10);
set_interrupt_gate(cpu, 11, &trap11);
set_interrupt_gate(cpu, 12, &trap12);
set_interrupt_gate(cpu, 13, &trap13);
set_interrupt_gate(cpu, 14, &trap14);
set_interrupt_gate(cpu, 16, &trap16);
set_interrupt_gate(cpu, 17, &trap17);
set_interrupt_gate(cpu, 18, &trap18);
set_interrupt_gate(cpu, 19, &trap19);
set_interrupt_gate(cpu, 32, &trap32);
set_interrupt_gate(cpu, 33, &trap33);
set_interrupt_gate(cpu, 34, &trap34);
set_interrupt_gate(cpu, 35, &trap35);
set_interrupt_gate(cpu, 36, &trap36);
set_interrupt_gate(cpu, 37, &trap37);
set_interrupt_gate(cpu, 38, &trap38);
set_interrupt_gate(cpu, 39, &trap39);
set_interrupt_gate(cpu, 40, &trap40);
set_interrupt_gate(cpu, 41, &trap41);
set_interrupt_gate(cpu, 42, &trap42);
set_interrupt_gate(cpu, 43, &trap43);
set_interrupt_gate(cpu, 44, &trap44);
set_interrupt_gate(cpu, 45, &trap45);
set_interrupt_gate(cpu, 46, &trap46);
set_interrupt_gate(cpu, 47, &trap47);
set_interrupt_gate(cpu, 48, &trap48);
set_interrupt_gate(cpu, 49, &trap49);
set_interrupt_gate(cpu, 50, &trap50);
set_interrupt_gate(cpu, 51, &trap51);
set_interrupt_gate(cpu, 52, &trap52);
set_interrupt_gate(cpu, 53, &trap53);
set_interrupt_gate(cpu, 54, &trap54);
set_interrupt_gate(cpu, 55, &trap55);
set_interrupt_gate(cpu, 56, &trap56);
set_interrupt_gate(cpu, 57, &trap57);
set_interrupt_gate(cpu, 58, &trap58);
set_interrupt_gate(cpu, 59, &trap59);
set_interrupt_gate(cpu, 60, &trap60);
set_interrupt_gate(cpu, 61, &trap61);
set_interrupt_gate(cpu, 62, &trap62);
set_interrupt_gate(cpu, 63, &trap63);
set_interrupt_gate(cpu, 64, &trap64);
set_interrupt_gate(cpu, 65, &trap65);
set_interrupt_gate(cpu, 66, &trap66);
set_interrupt_gate(cpu, 67, &trap67);
set_interrupt_gate(cpu, 68, &trap68);
set_interrupt_gate(cpu, 69, &trap69);
set_interrupt_gate(cpu, 70, &trap70);
set_interrupt_gate(cpu, 71, &trap71);
set_interrupt_gate(cpu, 72, &trap72);
set_interrupt_gate(cpu, 73, &trap73);
set_interrupt_gate(cpu, 74, &trap74);
set_interrupt_gate(cpu, 75, &trap75);
set_interrupt_gate(cpu, 76, &trap76);
set_interrupt_gate(cpu, 77, &trap77);
set_interrupt_gate(cpu, 78, &trap78);
set_interrupt_gate(cpu, 79, &trap79);
set_interrupt_gate(cpu, 80, &trap80);
set_interrupt_gate(cpu, 81, &trap81);
set_interrupt_gate(cpu, 82, &trap82);
set_interrupt_gate(cpu, 83, &trap83);
set_interrupt_gate(cpu, 84, &trap84);
set_interrupt_gate(cpu, 85, &trap85);
set_interrupt_gate(cpu, 86, &trap86);
set_interrupt_gate(cpu, 87, &trap87);
set_interrupt_gate(cpu, 88, &trap88);
set_interrupt_gate(cpu, 89, &trap89);
set_interrupt_gate(cpu, 90, &trap90);
set_interrupt_gate(cpu, 91, &trap91);
set_interrupt_gate(cpu, 92, &trap92);
set_interrupt_gate(cpu, 93, &trap93);
set_interrupt_gate(cpu, 94, &trap94);
set_interrupt_gate(cpu, 95, &trap95);
set_interrupt_gate(cpu, 96, &trap96);
set_interrupt_gate(cpu, 97, &trap97);
set_trap_gate(cpu, 98, &trap98);
set_trap_gate(cpu, 99, &trap99);
set_interrupt_gate(cpu, 100, &trap100);
set_interrupt_gate(cpu, 101, &trap101);
set_interrupt_gate(cpu, 102, &trap102);
set_interrupt_gate(cpu, 103, &trap103);
set_interrupt_gate(cpu, 104, &trap104);
set_interrupt_gate(cpu, 105, &trap105);
set_interrupt_gate(cpu, 106, &trap106);
set_interrupt_gate(cpu, 107, &trap107);
set_interrupt_gate(cpu, 108, &trap108);
set_interrupt_gate(cpu, 109, &trap109);
set_interrupt_gate(cpu, 110, &trap110);
set_interrupt_gate(cpu, 111, &trap111);
set_interrupt_gate(cpu, 112, &trap112);
set_interrupt_gate(cpu, 113, &trap113);
set_interrupt_gate(cpu, 114, &trap114);
set_interrupt_gate(cpu, 115, &trap115);
set_interrupt_gate(cpu, 116, &trap116);
set_interrupt_gate(cpu, 117, &trap117);
set_interrupt_gate(cpu, 118, &trap118);
set_interrupt_gate(cpu, 119, &trap119);
set_interrupt_gate(cpu, 120, &trap120);
set_interrupt_gate(cpu, 121, &trap121);
set_interrupt_gate(cpu, 122, &trap122);
set_interrupt_gate(cpu, 123, &trap123);
set_interrupt_gate(cpu, 124, &trap124);
set_interrupt_gate(cpu, 125, &trap125);
set_interrupt_gate(cpu, 126, &trap126);
set_interrupt_gate(cpu, 127, &trap127);
set_interrupt_gate(cpu, 128, &trap128);
set_interrupt_gate(cpu, 129, &trap129);
set_interrupt_gate(cpu, 130, &trap130);
set_interrupt_gate(cpu, 131, &trap131);
set_interrupt_gate(cpu, 132, &trap132);
set_interrupt_gate(cpu, 133, &trap133);
set_interrupt_gate(cpu, 134, &trap134);
set_interrupt_gate(cpu, 135, &trap135);
set_interrupt_gate(cpu, 136, &trap136);
set_interrupt_gate(cpu, 137, &trap137);
set_interrupt_gate(cpu, 138, &trap138);
set_interrupt_gate(cpu, 139, &trap139);
set_interrupt_gate(cpu, 140, &trap140);
set_interrupt_gate(cpu, 141, &trap141);
set_interrupt_gate(cpu, 142, &trap142);
set_interrupt_gate(cpu, 143, &trap143);
set_interrupt_gate(cpu, 144, &trap144);
set_interrupt_gate(cpu, 145, &trap145);
set_interrupt_gate(cpu, 146, &trap146);
set_interrupt_gate(cpu, 147, &trap147);
set_interrupt_gate(cpu, 148, &trap148);
set_interrupt_gate(cpu, 149, &trap149);
set_interrupt_gate(cpu, 150, &trap150);
set_interrupt_gate(cpu, 151, &trap151);
set_interrupt_gate(cpu, 152, &trap152);
set_interrupt_gate(cpu, 153, &trap153);
set_interrupt_gate(cpu, 154, &trap154);
set_interrupt_gate(cpu, 155, &trap155);
set_interrupt_gate(cpu, 156, &trap156);
set_interrupt_gate(cpu, 157, &trap157);
set_interrupt_gate(cpu, 158, &trap158);
set_interrupt_gate(cpu, 159, &trap159);
set_interrupt_gate(cpu, 160, &trap160);
set_interrupt_gate(cpu, 161, &trap161);
set_interrupt_gate(cpu, 162, &trap162);
set_interrupt_gate(cpu, 163, &trap163);
set_interrupt_gate(cpu, 164, &trap164);
set_interrupt_gate(cpu, 165, &trap165);
set_interrupt_gate(cpu, 166, &trap166);
set_interrupt_gate(cpu, 167, &trap167);
set_interrupt_gate(cpu, 168, &trap168);
set_interrupt_gate(cpu, 169, &trap169);
set_interrupt_gate(cpu, 170, &trap170);
set_interrupt_gate(cpu, 171, &trap171);
set_interrupt_gate(cpu, 172, &trap172);
set_interrupt_gate(cpu, 173, &trap173);
set_interrupt_gate(cpu, 174, &trap174);
set_interrupt_gate(cpu, 175, &trap175);
set_interrupt_gate(cpu, 176, &trap176);
set_interrupt_gate(cpu, 177, &trap177);
set_interrupt_gate(cpu, 178, &trap178);
set_interrupt_gate(cpu, 179, &trap179);
set_interrupt_gate(cpu, 180, &trap180);
set_interrupt_gate(cpu, 181, &trap181);
set_interrupt_gate(cpu, 182, &trap182);
set_interrupt_gate(cpu, 183, &trap183);
set_interrupt_gate(cpu, 184, &trap184);
set_interrupt_gate(cpu, 185, &trap185);
set_interrupt_gate(cpu, 186, &trap186);
set_interrupt_gate(cpu, 187, &trap187);
set_interrupt_gate(cpu, 188, &trap188);
set_interrupt_gate(cpu, 189, &trap189);
set_interrupt_gate(cpu, 190, &trap190);
set_interrupt_gate(cpu, 191, &trap191);
set_interrupt_gate(cpu, 192, &trap192);
set_interrupt_gate(cpu, 193, &trap193);
set_interrupt_gate(cpu, 194, &trap194);
set_interrupt_gate(cpu, 195, &trap195);
set_interrupt_gate(cpu, 196, &trap196);
set_interrupt_gate(cpu, 197, &trap197);
set_interrupt_gate(cpu, 198, &trap198);
set_interrupt_gate(cpu, 199, &trap199);
set_interrupt_gate(cpu, 200, &trap200);
set_interrupt_gate(cpu, 201, &trap201);
set_interrupt_gate(cpu, 202, &trap202);
set_interrupt_gate(cpu, 203, &trap203);
set_interrupt_gate(cpu, 204, &trap204);
set_interrupt_gate(cpu, 205, &trap205);
set_interrupt_gate(cpu, 206, &trap206);
set_interrupt_gate(cpu, 207, &trap207);
set_interrupt_gate(cpu, 208, &trap208);
set_interrupt_gate(cpu, 209, &trap209);
set_interrupt_gate(cpu, 210, &trap210);
set_interrupt_gate(cpu, 211, &trap211);
set_interrupt_gate(cpu, 212, &trap212);
set_interrupt_gate(cpu, 213, &trap213);
set_interrupt_gate(cpu, 214, &trap214);
set_interrupt_gate(cpu, 215, &trap215);
set_interrupt_gate(cpu, 216, &trap216);
set_interrupt_gate(cpu, 217, &trap217);
set_interrupt_gate(cpu, 218, &trap218);
set_interrupt_gate(cpu, 219, &trap219);
set_interrupt_gate(cpu, 220, &trap220);
set_interrupt_gate(cpu, 221, &trap221);
set_interrupt_gate(cpu, 222, &trap222);
set_interrupt_gate(cpu, 223, &trap223);
set_interrupt_gate(cpu, 224, &trap224);
set_interrupt_gate(cpu, 225, &trap225);
set_interrupt_gate(cpu, 226, &trap226);
set_interrupt_gate(cpu, 227, &trap227);
set_interrupt_gate(cpu, 228, &trap228);
set_interrupt_gate(cpu, 229, &trap229);
set_interrupt_gate(cpu, 230, &trap230);
set_interrupt_gate(cpu, 231, &trap231);
set_interrupt_gate(cpu, 232, &trap232);
set_interrupt_gate(cpu, 233, &trap233);
set_interrupt_gate(cpu, 234, &trap234);
set_interrupt_gate(cpu, 235, &trap235);
set_interrupt_gate(cpu, 236, &trap236);
set_interrupt_gate(cpu, 237, &trap237);
set_interrupt_gate(cpu, 238, &trap238);
set_interrupt_gate(cpu, 239, &trap239);
set_interrupt_gate(cpu, 240, &trap240);
set_interrupt_gate(cpu, 241, &trap241);
set_interrupt_gate(cpu, 242, &trap242);
set_interrupt_gate(cpu, 243, &trap243);
set_interrupt_gate(cpu, 244, &trap244);
set_interrupt_gate(cpu, 245, &trap245);
set_interrupt_gate(cpu, 246, &trap246);
set_interrupt_gate(cpu, 247, &trap247);
set_interrupt_gate(cpu, 248, &trap248);
set_interrupt_gate(cpu, 249, &trap249);
set_interrupt_gate(cpu, 250, &trap250);
set_interrupt_gate(cpu, 251, &trap251);
set_interrupt_gate(cpu, 252, &trap252);
set_interrupt_gate(cpu, 253, &trap253);
set_interrupt_gate(cpu, 254, &trap254);
set_interrupt_gate(cpu, 255, &trap255);
load_idt(cpu);
}
void
x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu)
{
init_idt_percpu(args, cpu);
init_gdt_percpu(args, cpu);
}
void
x86_descriptors_init(kernel_args* args)
{
reserve_io_interrupt_vectors(2, 98, INTERRUPT_TYPE_SYSCALL);
interrupt_handler_function** table = gInterruptHandlerTable;
uint32 i;
for (i = 0; i < ARCH_INTERRUPT_BASE; i++)
table[i] = x86_invalid_exception;
for (i = ARCH_INTERRUPT_BASE; i < kInterruptHandlerTableSize; i++)
table[i] = x86_hardware_interrupt;
table[0] = x86_unexpected_exception;
table[1] = x86_handle_debug_exception;
table[2] = x86_fatal_exception;
table[3] = x86_handle_breakpoint_exception;
table[4] = x86_unexpected_exception;
table[5] = x86_unexpected_exception;
table[6] = x86_unexpected_exception;
table[7] = x86_fatal_exception;
table[8] = x86_double_fault_exception;
table[9] = x86_fatal_exception;
table[10] = x86_fatal_exception;
table[11] = x86_fatal_exception;
table[12] = x86_fatal_exception;
table[13] = x86_unexpected_exception;
table[14] = x86_page_fault_exception;
table[16] = x86_unexpected_exception;
table[17] = x86_unexpected_exception;
table[18] = x86_fatal_exception;
table[19] = x86_unexpected_exception;
}