///                MentOS, The Mentoring Operating system project
/// @file tss.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#include "descriptor_tables/tss.h"

#include "string.h"
#include "io/debug.h"
#include "descriptor_tables/gdt.h"

static tss_entry_t kernel_tss;

void tss_init(uint8_t idx, uint32_t ss0)
{
    uint32_t base  = (uint32_t)&kernel_tss;
    uint32_t limit = base + sizeof(tss_entry_t);

    // Add the TSS descriptor to the GDT.
    // Kernel tss, access(E9 = 1 11 0 1 0 0 1)
    //    1   present
    //    11  ring 3 (kernel)
    //    0   always 0 when dealing with system segments
    //    1   execution
    //    0   can not be executed by ring lower or equal to DPL,
    //    0   not readable
    //    1   access bit, always 0, cpu set this to 1 when accessing this sector
    gdt_set_gate(idx, base, limit, GDT_PRESENT | GDT_USER | GDT_EX | GDT_AC, 0x0);

    // Note that we usually set tss's esp to 0 when booting our os, however,
    // we need to set it to the real esp when we've switched to usermode
    // because the CPU needs to know what esp to use when usermode app is
    // calling a kernel function(aka system call), that's why we have a
    // function below called tss_set_stack.
    memset(&kernel_tss, 0x0, sizeof(tss_entry_t));
    kernel_tss.ss0   = ss0;
    kernel_tss.esp0  = 0x0;
    kernel_tss.cs    = 0x0b;
    kernel_tss.ds    = 0x13;
    kernel_tss.es    = 0x13;
    kernel_tss.fs    = 0x13;
    kernel_tss.gs    = 0x13;
    kernel_tss.ss    = 0x13;
    kernel_tss.iomap = sizeof(tss_entry_t);
}

void tss_set_stack(uint32_t kss, uint32_t kesp)
{
    // Kernel data segment.
    kernel_tss.ss0 = kss;
    // Kernel stack address.
    kernel_tss.esp0 = kesp;
}
