/* * Copyright 2010 Stefan Lankes, Chair for Operating Systems, * RWTH Aachen University * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file is part of MetalSVM. */ #include #include #include #include #include gdt_ptr_t gp; static tss_t task_state_segments[MAX_TASKS]; static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}}; static unsigned char kstacks[MAX_TASKS][KERNEL_STACK_SIZE]; /* * This is in start.asm. We use this to properly reload * the new segment registers */ extern void gdt_flush(void); int register_task(task_t* task) { uint16_t sel = (task->id+5) << 3; asm volatile ("mov %0, %%ax; ltr %%ax" : : "ir"(sel)); return 0; } int create_default_frame(task_t* task, entry_point_t ep, void* arg, int user) { uint16_t cs = user ? 0x1B : 0x08; uint16_t ds = user ? 0x23 : 0x10; uint32_t id; if (BUILTIN_EXPECT(!task, 0)) return -1; if (BUILTIN_EXPECT(user && !task->ustack, 0)) return -1; if (BUILTIN_EXPECT(user && !task->stack_size, 0)) return -1; id = task->id; /* reset buffers */ memset(task_state_segments+id, 0x00, sizeof(tss_t)); memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE); if (user) memset(task->ustack, 0XCD, task->stack_size); /* set default values of all regsiters */ task_state_segments[id].cs = cs; task_state_segments[id].ss = ds; task_state_segments[id].ds = ds; task_state_segments[id].fs = ds; task_state_segments[id].gs = ds; task_state_segments[id].es = ds; task_state_segments[id].eflags = 0x1202; task_state_segments[id].eip = (uint32_t) ep; if (user) task_state_segments[id].esp = (uint32_t) task->ustack + task->stack_size - sizeof(size_t); else task_state_segments[id].esp = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); /* build default stack frame */ *((size_t*)task_state_segments[id].esp) = 0xDEADBEAF; /* dead-end */ task_state_segments[id].ebp = task_state_segments[id].esp; task_state_segments[id].esp -= sizeof(size_t); *((size_t*)task_state_segments[id].esp) = (size_t) arg; task_state_segments[id].esp -= sizeof(size_t); if (user) *((size_t*)task_state_segments[id].esp) = (size_t) leave_user_task; else *((size_t*)task_state_segments[id].esp) = (size_t) leave_kernel_task; /* setup for the kernel stack frame */ task_state_segments[id].ss0 = 0x10; if (user) task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); else task_state_segments[id].esp0 = task_state_segments[id].esp; return 0; } /* Setup a descriptor in the Global Descriptor Table */ static void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) { /* Setup the descriptor base address */ gdt[num].base_low = (base & 0xFFFF); gdt[num].base_middle = (base >> 16) & 0xFF; gdt[num].base_high = (base >> 24) & 0xFF; /* Setup the descriptor limits */ gdt[num].limit_low = (limit & 0xFFFF); gdt[num].granularity = ((limit >> 16) & 0x0F); /* Finally, set up the granularity and access flags */ gdt[num].granularity |= (gran & 0xF0); gdt[num].access = access; } /* * This will setup the special GDT * pointer, set up the entries in our GDT, and then * finally call gdt_flush() in our assembler file in order * to tell the processor where the new GDT is and update the * new segment registers */ void gdt_install(void) { unsigned int i; memset(task_state_segments, 0x00, MAX_TASKS*sizeof(tss_t)); /* Setup the GDT pointer and limit */ gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1; gp.base = (unsigned int) &gdt; /* Our NULL descriptor */ gdt_set_gate(0, 0, 0, 0, 0); /* * The second entry is our Code Segment. The base address * is 0, the limit is 4 GByte, it uses 4KByte granularity, * uses 32-bit opcodes, and is a Code Segment descriptor. */ gdt_set_gate(1, 0, 0xFFFFFFFF, GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); /* * The third entry is our Data Segment. It's EXACTLY the * same as our code segment, but the descriptor type in * this entry's access byte says it's a Data Segment */ gdt_set_gate(2, 0, 0xFFFFFFFF, GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); /* * Create code segement for userspace applications (ring 3) */ gdt_set_gate(3, 0, 0xFFFFFFFF, GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); /* * Create data segement for userspace applications (ring 3) */ gdt_set_gate(4, 0, 0xFFFFFFFF, GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); /* * Create a TSS for each task (we use these segments for task switching) */ for(i=0; i