/* * 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 #include #include #include gdt_ptr_t gp; static tss_t task_state_segments[MAX_TASKS] __attribute__ ((aligned (PAGE_SIZE))); static unsigned char kstacks[MAX_TASKS][KERNEL_STACK_SIZE] __attribute__ ((aligned (PAGE_SIZE), section (".data"))); // currently, our kernel has full access to the ioports static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}}; unsigned char* default_stack_pointer __attribute__ ((section (".data"))) = kstacks[0] + KERNEL_STACK_SIZE - sizeof(size_t); /* * 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; uint32_t id = task->id; if (BUILTIN_EXPECT(!task, 0)) return -EINVAL; sel = (task->id+5) << 3; asm volatile ("mov %0, %%ax; ltr %%ax" : : "ir"(sel) : "%eax"); // initialize the static elements of a TSS task_state_segments[id].cr3 = (uint32_t) (task->pgd); task_state_segments[id].ss0 = 0x10; return 0; } int arch_fork(task_t* task) { uint16_t cs = 0x08; uint16_t ds = 0x10; uint32_t id; task_t* curr_task = per_core(current_task); if (BUILTIN_EXPECT(!task, 0)) return -EINVAL; id = task->id; // copy kernel stack of the current task memcpy(kstacks[id], kstacks[curr_task->id], KERNEL_STACK_SIZE); // reset TSS memset(task_state_segments+id, 0x00, sizeof(tss_t)); // set default values of all registers 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].cr3 = (uint32_t) (virt_to_phys((size_t)task->pgd)); task_state_segments[id].ss0 = ds; task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); // save curret task context asm volatile("mov %%esp, %0" : "=r"(task_state_segments[id].esp)); task_state_segments[id].esp -= (uint32_t) kstacks[curr_task->id]; task_state_segments[id].esp += (uint32_t) kstacks[id]; asm volatile ("pusha"); asm volatile ("pop %0" : "=r"(task_state_segments[id].edi)); asm volatile ("pop %0" : "=r"(task_state_segments[id].esi)); asm volatile ("pop %0" : "=r"(task_state_segments[id].ebp)); #ifdef WITH_FRAME_POINTER task_state_segments[id].ebp -= (uint32_t) kstacks[curr_task->id]; task_state_segments[id].ebp += (uint32_t) kstacks[id]; #endif asm volatile ("add $4, %%esp" ::: "%esp"); asm volatile ("pop %0" : "=r"(task_state_segments[id].ebx)); asm volatile ("pop %0" : "=r"(task_state_segments[id].edx)); asm volatile ("pop %0" : "=r"(task_state_segments[id].ecx)); asm volatile ("pop %0" : "=r"(task_state_segments[id].eax)); // store current EFLAGS and set IF flag // => the parent task will enable the interrupt handling asm volatile ("pushf; pop %%eax; or $2,%%ah" : "=a"(task_state_segments[id].eflags)); // This will be the entry point for the new task. asm volatile ("call read_eip" : "=a"(task_state_segments[id].eip)); return 0; } int create_default_frame(task_t* task, entry_point_t ep, void* arg) { uint16_t cs = 0x08; uint16_t ds = 0x10; uint32_t id; if (BUILTIN_EXPECT(!task, 0)) return -EINVAL; id = task->id; /* reset buffers */ memset(task_state_segments+id, 0x00, sizeof(tss_t)); memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE); /* set default values of all registers */ 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].cr3 = (uint32_t) (virt_to_phys((size_t)task->pgd)); task_state_segments[id].eip = (uint32_t) ep; 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); *((size_t*)task_state_segments[id].esp) = (size_t) leave_kernel_task; /* setup for the kernel stack frame */ task_state_segments[id].ss0 = 0x10; task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); 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) { gdt[num] = configure_gdt_entry(base, limit, access, gran); } gdt_entry_t configure_gdt_entry(unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) { gdt_entry_t desc; /* Setup the descriptor base address */ desc.base_low = (base & 0xFFFF); desc.base_middle = (base >> 16) & 0xFF; desc.base_high = (base >> 24) & 0xFF; /* Setup the descriptor limits */ desc.limit_low = (limit & 0xFFFF); desc.granularity = ((limit >> 16) & 0x0F); /* Finally, set up the granularity and access flags */ desc.granularity |= (gran & 0xF0); desc.access = access; return desc; } /* * 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 TSS for each task at ring0 (we use these segments for task switching) */ for(i=0; i