2015-05-23 14:35:45 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2010, Stefan Lankes, RWTH Aachen University
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this
|
|
|
|
* software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <hermit/stdio.h>
|
|
|
|
#include <hermit/string.h>
|
|
|
|
#include <hermit/stdlib.h>
|
|
|
|
#include <hermit/tasks.h>
|
|
|
|
#include <hermit/errno.h>
|
|
|
|
#include <hermit/processor.h>
|
2017-11-04 12:46:57 +01:00
|
|
|
#include <hermit/logging.h>
|
2015-05-23 14:35:45 +02:00
|
|
|
#include <asm/gdt.h>
|
|
|
|
#include <asm/tss.h>
|
|
|
|
#include <asm/page.h>
|
|
|
|
|
2017-12-31 11:20:14 +01:00
|
|
|
// minimal number of GDT entries (for one core)
|
|
|
|
// a TSS descriptor is twice larger than a code/data descriptor
|
|
|
|
#define GDT_MIN_ENTRIES (7+1*2)
|
|
|
|
|
2015-07-19 11:12:05 +02:00
|
|
|
gdt_ptr_t gp;
|
2017-12-31 11:20:14 +01:00
|
|
|
|
2015-05-23 14:35:45 +02:00
|
|
|
// currently, our kernel has full access to the ioports
|
2017-12-31 11:20:14 +01:00
|
|
|
static gdt_entry_t boot_gdt[GDT_MIN_ENTRIES] = {[0 ... GDT_MIN_ENTRIES-1] = {0, 0, 0, 0, 0, 0}};
|
|
|
|
static gdt_entry_t* gdt = boot_gdt;
|
|
|
|
|
|
|
|
static tss_t* boot_tss = NULL;
|
|
|
|
static tss_t** task_state_segments = &boot_tss;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-03-13 14:07:20 +01:00
|
|
|
/*
|
2015-05-23 14:35:45 +02:00
|
|
|
* This is defined in entry.asm. We use this to properly reload
|
|
|
|
* the new segment registers
|
|
|
|
*/
|
|
|
|
extern void gdt_flush(void);
|
|
|
|
|
2017-11-26 12:48:00 +01:00
|
|
|
/*
|
|
|
|
* This is defined in entry.asm. We use this to properly replace
|
|
|
|
* the current stack
|
|
|
|
*/
|
|
|
|
extern void replace_boot_stack(size_t);
|
|
|
|
|
|
|
|
extern int32_t boot_processor;
|
2017-12-31 11:20:14 +01:00
|
|
|
extern atomic_int32_t possible_cpus;
|
2017-11-26 12:48:00 +01:00
|
|
|
extern atomic_int32_t current_boot_id;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-06-03 06:24:46 +02:00
|
|
|
void set_tss(size_t rps0, size_t ist1)
|
2015-09-12 17:23:59 +02:00
|
|
|
{
|
2017-11-26 12:48:00 +01:00
|
|
|
task_state_segments[CORE_ID]->rsp0 = rps0;
|
|
|
|
task_state_segments[CORE_ID]->ist1 = ist1;
|
2015-09-12 17:23:59 +02:00
|
|
|
}
|
|
|
|
|
2015-05-23 14:35:45 +02:00
|
|
|
/* Setup a descriptor in the Global Descriptor Table */
|
|
|
|
void gdt_set_gate(int num, unsigned long base, unsigned long limit,
|
|
|
|
unsigned char access, unsigned char gran)
|
|
|
|
{
|
2017-12-31 11:20:14 +01:00
|
|
|
configure_gdt_entry(gdt+num, base, limit, access, gran);
|
2015-05-23 14:35:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned long limit,
|
|
|
|
unsigned char access, unsigned char gran)
|
|
|
|
{
|
|
|
|
/* Setup the descriptor base address */
|
|
|
|
dest_entry->base_low = (base & 0xFFFF);
|
|
|
|
dest_entry->base_middle = (base >> 16) & 0xFF;
|
|
|
|
dest_entry->base_high = (base >> 24) & 0xFF;
|
|
|
|
|
|
|
|
/* Setup the descriptor limits */
|
|
|
|
dest_entry->limit_low = (limit & 0xFFFF);
|
|
|
|
dest_entry->granularity = ((limit >> 16) & 0x0F);
|
|
|
|
|
|
|
|
/* Finally, set up the granularity and access flags */
|
|
|
|
dest_entry->granularity |= (gran & 0xF0);
|
|
|
|
dest_entry->access = access;
|
|
|
|
}
|
|
|
|
|
2016-03-13 14:07:20 +01:00
|
|
|
/*
|
2015-05-23 14:35:45 +02:00
|
|
|
* 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
|
2016-03-13 14:07:20 +01:00
|
|
|
* new segment registers
|
2015-05-23 14:35:45 +02:00
|
|
|
*/
|
|
|
|
void gdt_install(void)
|
|
|
|
{
|
2017-11-26 12:48:00 +01:00
|
|
|
int num = 0;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
/* Setup the GDT pointer and limit */
|
2017-12-31 11:20:14 +01:00
|
|
|
gp.limit = (sizeof(gdt_entry_t) * GDT_MIN_ENTRIES) - 1;
|
|
|
|
gp.base = (size_t) gdt;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
/* Our NULL descriptor */
|
|
|
|
gdt_set_gate(num++, 0, 0, 0, 0);
|
|
|
|
|
2016-03-13 14:07:20 +01:00
|
|
|
/*
|
2015-05-23 14:35:45 +02:00
|
|
|
* The second entry is our Code Segment. The base address
|
|
|
|
* is 0, the limit is 4 GByte, it uses 4KByte granularity,
|
|
|
|
* and is a Code Segment descriptor.
|
|
|
|
*/
|
2015-08-17 17:07:34 +02:00
|
|
|
gdt_set_gate(num++, 0, 0,
|
|
|
|
GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_64_BIT);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-03-13 14:07:20 +01:00
|
|
|
/*
|
2015-05-23 14:35:45 +02:00
|
|
|
* The third entry is our Data Segment. It's EXACTLY the
|
|
|
|
* same as our code segment, but the descriptor type in
|
2016-03-13 14:07:20 +01:00
|
|
|
* this entry's access byte says it's a Data Segment
|
2015-05-23 14:35:45 +02:00
|
|
|
*/
|
2015-08-17 17:07:34 +02:00
|
|
|
gdt_set_gate(num++, 0, 0,
|
2017-08-21 00:45:25 +02:00
|
|
|
GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, GDT_FLAG_64_BIT);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create code segment for 32bit user-space applications (ring 3)
|
|
|
|
*/
|
|
|
|
gdt_set_gate(num++, 0, 0xFFFFFFFF,
|
|
|
|
GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_32_BIT | GDT_FLAG_4K_GRAN);
|
|
|
|
|
|
|
|
/*
|
2015-08-18 00:16:28 +02:00
|
|
|
* Create data segment for 32bit user-space applications (ring 3)
|
2015-05-23 14:35:45 +02:00
|
|
|
*/
|
2015-08-17 17:07:34 +02:00
|
|
|
gdt_set_gate(num++, 0, 0xFFFFFFFF,
|
|
|
|
GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, GDT_FLAG_32_BIT | GDT_FLAG_4K_GRAN);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create code segment for 64bit user-space applications (ring 3)
|
|
|
|
*/
|
2015-08-17 17:07:34 +02:00
|
|
|
gdt_set_gate(num++, 0, 0,
|
|
|
|
GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_64_BIT);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create data segment for 64bit user-space applications (ring 3)
|
|
|
|
*/
|
|
|
|
gdt_set_gate(num++, 0, 0,
|
2017-08-21 00:45:25 +02:00
|
|
|
GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, GDT_FLAG_64_BIT);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
/* Flush out the old GDT and install the new changes! */
|
|
|
|
gdt_flush();
|
|
|
|
}
|
2017-11-04 12:46:57 +01:00
|
|
|
|
2017-11-26 12:48:00 +01:00
|
|
|
/*
|
|
|
|
* Create TSS for the current core (we use these segments for task switching)
|
|
|
|
*/
|
|
|
|
void tss_init(tid_t id /* => current task id */)
|
2017-11-04 12:46:57 +01:00
|
|
|
{
|
2017-12-31 11:20:14 +01:00
|
|
|
int32_t no_cpus = atomic_int32_read(&possible_cpus);
|
|
|
|
int32_t core_id = atomic_int32_read(¤t_boot_id);
|
|
|
|
|
|
|
|
LOG_INFO("Initialize TSS for task %d on core %d, possible cores %d\n",
|
|
|
|
id, core_id, no_cpus);
|
|
|
|
|
|
|
|
if ((task_state_segments == &boot_tss) && (no_cpus > 1))
|
|
|
|
{
|
|
|
|
task_state_segments = (tss_t**) kmalloc(sizeof(tss_t*)*no_cpus);
|
|
|
|
if (BUILTIN_EXPECT(!task_state_segments, 0)) {
|
|
|
|
LOG_ERROR("Unable to allocate array for the task state segments\n");
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(task_state_segments, 0x00, sizeof(tss_t*)*no_cpus);
|
|
|
|
task_state_segments[0] = boot_tss;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((gdt == boot_gdt) && (no_cpus > 1))
|
|
|
|
{
|
|
|
|
gdt = (gdt_entry_t*) kmalloc(sizeof(gdt_entry_t)*(7+no_cpus*2));
|
|
|
|
if (BUILTIN_EXPECT(!gdt, 0)) {
|
|
|
|
LOG_ERROR("Unable to allocate GDT\n");
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(gdt, 0x00, sizeof(gdt_entry_t)*(7+no_cpus*2));
|
|
|
|
memcpy(gdt, &boot_gdt, sizeof(gdt_entry_t)*GDT_MIN_ENTRIES);
|
|
|
|
|
|
|
|
gp.limit = (sizeof(gdt_entry_t) * (7+no_cpus*2)) - 1;
|
|
|
|
gp.base = (size_t) gdt;
|
|
|
|
}
|
2017-11-26 12:48:00 +01:00
|
|
|
|
|
|
|
tss_t* tss = (tss_t*) kmalloc(sizeof(tss_t));
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!tss, 0)) {
|
|
|
|
LOG_ERROR("Unable to allocate task state segment\n");
|
2017-11-26 12:48:00 +01:00
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(tss, 0x00, sizeof(tss_t));
|
|
|
|
|
|
|
|
size_t rsp0 = (size_t) create_stack(KERNEL_STACK_SIZE);
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!rsp0, 0)) {
|
2017-11-26 12:48:00 +01:00
|
|
|
LOG_ERROR("Unable to allocate stack for the idle task %d\n", id);
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
tss->rsp0 = rsp0 + KERNEL_STACK_SIZE - 0x10;
|
|
|
|
|
|
|
|
size_t ist1 = (size_t) create_stack(KERNEL_STACK_SIZE);
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!ist1, 0)) {
|
2017-11-26 12:48:00 +01:00
|
|
|
LOG_ERROR("Unable to allocate ist1 for the idle task %d\n", id);
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
tss->ist1 = (size_t) ist1 + KERNEL_STACK_SIZE - 0x10;
|
|
|
|
|
|
|
|
tss->ist2 = (size_t) create_stack(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE - 0x10;
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!tss->ist2, 0)) {
|
2017-11-26 12:48:00 +01:00
|
|
|
LOG_ERROR("Unable to allocate ist2 for the idle task %d\n", id);
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
|
|
|
|
tss->ist3 = (size_t) create_stack(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE - 0x10;
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!tss->ist3, 0)) {
|
2017-11-26 12:48:00 +01:00
|
|
|
LOG_ERROR("Unable to allocate ist3 for the idle task %d\n", id);
|
|
|
|
goto oom;
|
2017-11-04 12:46:57 +01:00
|
|
|
}
|
2017-11-26 12:48:00 +01:00
|
|
|
|
|
|
|
tss->ist4 = (size_t) create_stack(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE - 0x10;
|
2017-12-31 11:20:14 +01:00
|
|
|
if (BUILTIN_EXPECT(!tss->ist4, 0)) {
|
2017-11-26 12:48:00 +01:00
|
|
|
LOG_ERROR("Unable to allocate ist4 for the idle task %d\n", id);
|
|
|
|
goto oom;
|
|
|
|
}
|
|
|
|
|
2017-12-31 11:20:14 +01:00
|
|
|
task_state_segments[core_id] = tss;
|
|
|
|
gdt_set_gate(7+core_id*2, (unsigned long) tss,
|
2017-11-26 12:48:00 +01:00
|
|
|
sizeof(tss_t), GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0, 0);
|
|
|
|
|
|
|
|
// set stack in our task table
|
|
|
|
set_boot_stack(id, rsp0, ist1);
|
|
|
|
|
|
|
|
// replace the stack pointer
|
|
|
|
replace_boot_stack(rsp0);
|
|
|
|
|
2017-12-31 11:20:14 +01:00
|
|
|
gdt_flush();
|
|
|
|
reset_fsgs(core_id);
|
|
|
|
|
2017-11-26 12:48:00 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
oom:
|
|
|
|
while(1) { HALT; }
|
2017-11-04 12:46:57 +01:00
|
|
|
}
|