WIP: add TSS management
This commit is contained in:
parent
8a53ecfecd
commit
8f6f6cf471
@ -1,15 +1,16 @@
|
||||
/* Copyright (C) 2005 David Decotigny
|
||||
/* Copyright (C) 2021 Mathieu Maret
|
||||
Copyright (C) 2005 David Decotigny
|
||||
Copyright (C) 2000-2004, The KOS team
|
||||
|
||||
Initially taken from SOS
|
||||
*/
|
||||
|
||||
#include "cpu_context.h"
|
||||
#include "assert.h"
|
||||
#include "gdt.h"
|
||||
#include "klibc.h"
|
||||
#include "segment.h"
|
||||
|
||||
#include "cpu_context.h"
|
||||
|
||||
/**
|
||||
* Here is the definition of a CPU context for IA32 processors. This
|
||||
* is a Matos/SOS convention, not a specification given by the IA32
|
||||
@ -76,6 +77,22 @@ struct cpu_kstate {
|
||||
struct cpu_state regs;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* Structure of an interrupted User thread's context. This is almost
|
||||
* the same as a kernel context, except that 2 additional values are
|
||||
* pushed on the stack before the eflags/cs/eip of the interrupted
|
||||
* context: the stack configuration of the interrupted user context.
|
||||
*
|
||||
* @see Section 6.4.1 of Intel x86 vol 1
|
||||
*/
|
||||
struct cpu_ustate {
|
||||
struct cpu_state regs;
|
||||
struct {
|
||||
uint32_t cpl3_esp;
|
||||
uint16_t cpl3_ss;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* THE main operation of a kernel thread. This routine calls the
|
||||
* kernel thread function start_func and calls exit_func when
|
||||
@ -96,6 +113,123 @@ static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure of a Task State Segment on the x86 Architecture.
|
||||
*
|
||||
* @see Intel x86 spec vol 3, figure 6-2
|
||||
*
|
||||
* @note Such a data structure should not cross any page boundary (see
|
||||
* end of section 6.2.1 of Intel spec vol 3). This is the reason why
|
||||
* we tell gcc to align it on a 128B boundary (its size is 104B, which
|
||||
* is <= 128).
|
||||
*/
|
||||
struct x86_tss {
|
||||
|
||||
/**
|
||||
* Intel provides a way for a task to switch to another in an
|
||||
* automatic way (call gates). In this case, the back_link field
|
||||
* stores the source TSS of the context switch. This allows to
|
||||
* easily implement coroutines, task backtracking, ... In Matos/SOS we
|
||||
* don't use TSS for the context switch purpouse, so we always
|
||||
* ignore this field.
|
||||
* (+0)
|
||||
*/
|
||||
uint16_t back_link;
|
||||
|
||||
uint16_t reserved1;
|
||||
|
||||
/* CPL0 saved context. (+4) */
|
||||
vaddr_t esp0;
|
||||
uint16_t ss0;
|
||||
|
||||
uint16_t reserved2;
|
||||
|
||||
/* CPL1 saved context. (+12) */
|
||||
vaddr_t esp1;
|
||||
uint16_t ss1;
|
||||
|
||||
uint16_t reserved3;
|
||||
|
||||
/* CPL2 saved context. (+20) */
|
||||
vaddr_t esp2;
|
||||
uint16_t ss2;
|
||||
|
||||
uint16_t reserved4;
|
||||
|
||||
/* Interrupted context's saved registers. (+28) */
|
||||
vaddr_t cr3;
|
||||
vaddr_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t eax;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t ebx;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
|
||||
/* +72 */
|
||||
uint16_t es;
|
||||
uint16_t reserved5;
|
||||
|
||||
/* +76 */
|
||||
uint16_t cs;
|
||||
uint16_t reserved6;
|
||||
|
||||
/* +80 */
|
||||
uint16_t ss;
|
||||
uint16_t reserved7;
|
||||
|
||||
/* +84 */
|
||||
uint16_t ds;
|
||||
uint16_t reserved8;
|
||||
|
||||
/* +88 */
|
||||
uint16_t fs;
|
||||
uint16_t reserved9;
|
||||
|
||||
/* +92 */
|
||||
uint16_t gs;
|
||||
uint16_t reserved10;
|
||||
|
||||
/* +96 */
|
||||
uint16_t ldtr;
|
||||
uint16_t reserved11;
|
||||
|
||||
/* +100 */
|
||||
uint16_t debug_trap_flag : 1;
|
||||
uint16_t reserved12 : 15;
|
||||
uint16_t iomap_base_addr;
|
||||
|
||||
/* 104 */
|
||||
} __attribute__((packed, aligned(128)));
|
||||
|
||||
static struct x86_tss kernel_tss;
|
||||
|
||||
int cpu_context_subsystem_setup()
|
||||
{
|
||||
/* Reset the kernel TSS */
|
||||
memset(&kernel_tss, 0x0, sizeof(kernel_tss));
|
||||
|
||||
/**
|
||||
* Now setup the kernel TSS.
|
||||
*
|
||||
* Considering the privilege change method we choose (cpl3 -> cpl0
|
||||
* through a software interrupt), we don't need to initialize a
|
||||
* full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually,
|
||||
* only a correct value for the kernel esp and ss are required (aka
|
||||
* "ss0" and "esp0" fields). Since the esp0 will have to be updated
|
||||
* at privilege change time, we don't have to set it up now.
|
||||
*/
|
||||
kernel_tss.ss0 = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA);
|
||||
|
||||
/* Register this TSS into the gdt */
|
||||
gdtRegisterTSS((vaddr_t)&kernel_tss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func,
|
||||
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||||
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg)
|
||||
@ -213,6 +347,30 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_
|
||||
/* =======================================================================
|
||||
* Public Accessor functions
|
||||
*/
|
||||
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt)
|
||||
{
|
||||
/* An interrupted user thread has its CS register set to that of the
|
||||
User code segment */
|
||||
switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs)) {
|
||||
case BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE):
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
case BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE):
|
||||
return FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !",
|
||||
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
|
||||
BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE),
|
||||
BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Should never get here */
|
||||
return -1;
|
||||
}
|
||||
|
||||
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt)
|
||||
{
|
||||
@ -240,3 +398,37 @@ void cpu_context_dump(const struct cpu_state *ctxt)
|
||||
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
|
||||
(unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code);
|
||||
}
|
||||
|
||||
/* *************************************************************
|
||||
* Function to manage the TSS. This function is not really "public":
|
||||
* it is reserved to the assembler routines defined in
|
||||
* cpu_context_switch.S
|
||||
*
|
||||
* Update the kernel stack address so that the IRQ, syscalls and
|
||||
* exception return in a correct stack location when coming back into
|
||||
* kernel mode.
|
||||
*/
|
||||
void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
|
||||
{
|
||||
/* next_ctxt corresponds to an interrupted user thread ? */
|
||||
if (cpu_context_is_in_user_mode(next_ctxt)) {
|
||||
/*
|
||||
* Yes: "next_ctxt" is an interrupted user thread => we are
|
||||
* going to switch to user mode ! Setup the stack address so
|
||||
* that the user thread "next_ctxt" can come back to the correct
|
||||
* stack location when returning in kernel mode.
|
||||
*
|
||||
* This stack location corresponds to the SP of the next user
|
||||
* thread once its context has been transferred on the CPU, ie
|
||||
* once the CPU has executed all the pop/iret instruction of the
|
||||
* context switch with privilege change.
|
||||
*/
|
||||
kernel_tss.esp0 = ((vaddr_t)next_ctxt) + sizeof(struct cpu_ustate);
|
||||
/* Note: no need to protect this agains IRQ because IRQs are not
|
||||
allowed to update it by themselves, and they are not allowed
|
||||
to block */
|
||||
} else {
|
||||
/* No: No need to update kernel TSS when we stay in kernel
|
||||
mode */
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,17 @@
|
||||
|
||||
.text
|
||||
|
||||
/**
|
||||
* C Function called by the routines below in order to tell the CPU
|
||||
* where will be the kernel stack (needed by the interrupt handlers)
|
||||
* when next_ctxt will come back into kernel mode.
|
||||
*
|
||||
* void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
|
||||
*
|
||||
* @see end of cpu_context.c
|
||||
*/
|
||||
.extern cpu_context_update_kernel_tss
|
||||
|
||||
|
||||
.globl cpu_context_switch
|
||||
.type cpu_context_switch, @function
|
||||
@ -32,6 +43,15 @@ cpu_context_switch:
|
||||
/* This is the proper context switch ! We change the stack here */
|
||||
movl 68(%esp), %esp
|
||||
|
||||
/* Prepare kernel TSS in case we are switching to a user thread: we
|
||||
make sure that we will come back into the kernel at a correct
|
||||
stack location */
|
||||
pushl %esp /* Pass the location of the context we are
|
||||
restoring to the function */
|
||||
call cpu_context_update_kernel_tss
|
||||
|
||||
|
||||
addl $4, %esp
|
||||
/* Restore the CPU context */
|
||||
popw %gs
|
||||
popw %fs
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2004 David Decotigny
|
||||
/* Copyright (C) 2021 Mathieu Maret
|
||||
Copyright (C) 2004 David Decotigny
|
||||
Copyright (C) 1999 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -17,7 +18,6 @@
|
||||
USA.
|
||||
*/
|
||||
#include "segment.h"
|
||||
|
||||
#include "gdt.h"
|
||||
|
||||
/**
|
||||
@ -111,6 +111,10 @@ static struct x86_segment_descriptor gdt[] = {
|
||||
},
|
||||
[SEG_KCODE] = BUILD_GDTE(0, 1),
|
||||
[SEG_KDATA] = BUILD_GDTE(0, 0),
|
||||
[SEG_UCODE] = BUILD_GDTE(3, 1),
|
||||
[SEG_UDATA] = BUILD_GDTE(3, 0),
|
||||
[SEG_K_TSS] = {0,}, // Used by syscall, IRQ while in user space
|
||||
// initialized by gdtRegisterTSS
|
||||
};
|
||||
|
||||
int gdtSetup(void)
|
||||
@ -144,3 +148,29 @@ int gdtSetup(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gdtRegisterTSS(vaddr_t tss_vaddr)
|
||||
{
|
||||
uint16_t regval_tss;
|
||||
|
||||
/* Initialize the GDT entry */
|
||||
gdt[SEG_K_TSS] = (struct x86_segment_descriptor){
|
||||
.limit_15_0 = 0x67, /* See Intel x86 vol 3 section 6.2.2 */
|
||||
.base_paged_addr_15_0 = (tss_vaddr)&0xffff,
|
||||
.base_paged_addr_23_16 = (tss_vaddr >> 16) & 0xff,
|
||||
.segment_type = 0x9, /* See Intel x86 vol 3 figure 6-3 */
|
||||
.descriptor_type = 0, /* (idem) */
|
||||
.dpl = 3, /* Allowed for CPL3 tasks */
|
||||
.present = 1,
|
||||
.limit_19_16 = 0, /* Size of a TSS is < 2^16 ! */
|
||||
.custom = 0, /* Unused */
|
||||
.op_size = 0, /* See Intel x86 vol 3 figure 6-3 */
|
||||
.granularity = 1, /* limit is in Bytes */
|
||||
.base_paged_addr_31_24 = (tss_vaddr >> 24) & 0xff};
|
||||
|
||||
/* Load the TSS register into the processor */
|
||||
regval_tss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_K_TSS);
|
||||
asm("ltr %0" : : "r"(regval_tss));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2004 David Decotigny
|
||||
/* Copyright (C) 2021 Mathieu Maret
|
||||
Copyright (C) 2004 David Decotigny
|
||||
Copyright (C) 1999 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -17,7 +18,7 @@
|
||||
USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
/**
|
||||
* @file gdt.h
|
||||
*
|
||||
@ -34,3 +35,4 @@
|
||||
* address space (ie "flat" virtual space).
|
||||
*/
|
||||
int gdtSetup(void);
|
||||
int gdtRegisterTSS(vaddr_t tss_vaddr);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2005 David Decotigny
|
||||
/* Copyright (C) 2021 Mathieu Maret
|
||||
Copyright (C) 2005 David Decotigny
|
||||
Copyright (C) 2000-2004, The KOS team
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -84,6 +85,11 @@ int cpu_kstate_init(struct cpu_state **kctxt, cpu_kstate_function_arg1_t *start_
|
||||
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||||
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg);
|
||||
|
||||
/**
|
||||
* Prepare the system to deal with multiple CPU execution contexts
|
||||
*/
|
||||
int cpu_context_subsystem_setup();
|
||||
|
||||
/**
|
||||
* Function that performs an immediate context-switch from one
|
||||
* kernel/user thread to another one. It stores the current executing
|
||||
@ -119,6 +125,14 @@ void cpu_context_exit_to(struct cpu_state *switch_to_ctxt,
|
||||
* Public Accessor functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return whether the saved context was in kernel or user context
|
||||
*
|
||||
* @return TRUE when context was interrupted when in user mode, FALSE
|
||||
* when in kernel mode, < 0 on error.
|
||||
*/
|
||||
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt);
|
||||
|
||||
/**
|
||||
* Return Program Counter stored in the saved kernel/user context
|
||||
*/
|
||||
@ -181,7 +195,8 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
||||
vaddr_t kernel_stack_bottom,
|
||||
size_t kernel_stack_size);
|
||||
#else
|
||||
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop \
|
||||
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) \
|
||||
({/* nop \
|
||||
*/})
|
||||
#define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */})
|
||||
#endif
|
||||
|
@ -129,6 +129,7 @@ void kmain(unsigned long magic, unsigned long addr)
|
||||
// Turns out linux and windows do the same !
|
||||
// https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/
|
||||
if (mmap[i].addr < 0x100000) {
|
||||
printf(" -> skipping\n");
|
||||
continue;
|
||||
}
|
||||
memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem),
|
||||
@ -161,7 +162,8 @@ void kmain(unsigned long magic, unsigned long addr)
|
||||
|
||||
printf("[Setup] allocation system\n");
|
||||
areaInit(firstUsedByMem, lastUsedByMem);
|
||||
//allocSetup();
|
||||
|
||||
cpu_context_subsystem_setup();
|
||||
|
||||
printf("[Setup] thread system\n");
|
||||
kthreadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
|
||||
|
@ -39,6 +39,9 @@
|
||||
#define SEG_NULL 0 /* NULL segment, unused by the procesor */
|
||||
#define SEG_KCODE 1 /* Kernel code segment */
|
||||
#define SEG_KDATA 2 /* Kernel data segment */
|
||||
#define SEG_UCODE 3 /* User code segment */
|
||||
#define SEG_UDATA 4 /* User data segment */
|
||||
#define SEG_K_TSS 5 /* Kernel TSS for priviledge change (user to kernel) */
|
||||
|
||||
/**
|
||||
* Helper macro that builds a segment register's value
|
||||
|
Loading…
Reference in New Issue
Block a user