Finish v0.3.0

This commit is contained in:
Luigi Capogrosso
2019-05-08 17:07:11 +02:00
parent 3d9beb5ee0
commit 7d9085fecf
330 changed files with 45755 additions and 0 deletions
+308
View File
@@ -0,0 +1,308 @@
/// MentOS, The Mentoring Operating system project
/// @file process.c
/// @brief Process data structures and functions.
/// @copyright (c) 2019 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "process.h"
#include "prio.h"
#include "init.h"
#include "panic.h"
#include "kheap.h"
#include "debug.h"
#include "unistd.h"
#include "string.h"
#include "list_head.h"
#include "stdatomic.h"
#include "scheduler.h"
#define PUSH_ON_STACK(stack, type, item) \
*((type *)(stack -= sizeof(type))) = item
/// @brief The task_struct of the init process.
static task_struct *init_proc;
void exit_handler()
{
exit(1);
kernel_panic("I should not be here.\n");
}
task_struct *create_init_process()
{
dbg_print("Building init process...\n");
// Create a new task_struct.
init_proc = kmalloc(sizeof(task_struct));
// TODO: process is IN USER SPACE! it should be in KERNEL SPACE!
memset(init_proc, 0, sizeof(task_struct));
// Set the id of the process.
init_proc->pid = get_new_pid();
// Set the name of the process.
strcpy(init_proc->name, "init");
// Set the statistics of the process.
init_proc->se.prio = DEFAULT_PRIO;
init_proc->se.start_runtime = 0;
init_proc->se.exec_start = 0;
init_proc->se.sum_exec_runtime = 0;
init_proc->se.vruntime = 0;
// Initialize the list_head.
list_head_init(&init_proc->run_list);
// Initialize the children list_head.
list_head_init(&init_proc->children);
// Initialize the sibling list_head.
list_head_init(&init_proc->sibling);
// Create a new stack segment.
init_proc->mm = create_process_image(DEFAULT_STACK_SIZE);
char *stack = (char *)init_proc->mm->start_stack;
// Clean stack space.
memset(stack, 0, DEFAULT_STACK_SIZE);
// Set the base address of the stack.
char *ebp = (char *)(stack + DEFAULT_STACK_SIZE);
// Create a pointer to keep track of the top of the stack.
char *esp = ebp;
// Set exit_handler as terminating function for init.
PUSH_ON_STACK(esp, uintptr_t, (uintptr_t)&exit_handler);
// Set the top address of the stack.
init_proc->thread.useresp = (uintptr_t)esp;
// Set the base address of the stack.
init_proc->thread.ebp = (uintptr_t)ebp;
// Set the program counter.
init_proc->thread.eip = (uintptr_t)&main_init;
// Enable the interrupts.
init_proc->thread.eflags = init_proc->thread.eflags | EFLAG_IF;
// Clear the current working directory.
memset(init_proc->cwd, '\0', MAX_PATH_LENGTH);
// Set the state of the process as running.
init_proc->state = TASK_RUNNING;
// Active the current process.
enqueue_task(init_proc);
dbg_print("--------------------------------------------------\n");
dbg_print("- %s process (PID: %d, eflags: %d)\n", init_proc->name,
init_proc->pid, init_proc->thread.eflags);
dbg_print("\tStack: [0x%p - 0x%p]\n", init_proc->mm->start_stack,
init_proc->mm->start_stack + DEFAULT_STACK_SIZE);
dbg_print("\tebp: 0x%p\n", init_proc->thread.ebp);
dbg_print("\tesp: 0x%p\n", init_proc->thread.useresp);
dbg_print("\teip: 0x%p\n", init_proc->thread.eip);
dbg_print("--------------------------------------------------\n");
return init_proc;
}
char *get_current_dir_name()
{
task_struct *current_process = kernel_get_current_process();
if (current_process != NULL) {
return strdup(current_process->cwd);
}
return kstrdup("/");
}
void sys_getcwd(char *path, size_t size)
{
task_struct *current_process = kernel_get_current_process();
if ((current_process != NULL) && (path != NULL)) {
strncpy(path, current_process->cwd, size);
}
}
void sys_chdir(char const *path)
{
task_struct *current_process = kernel_get_current_process();
if ((current_process != NULL) && (path != NULL)) {
strcpy(current_process->cwd, path);
}
}
pid_t sys_vfork(pt_regs *r)
{
task_struct *current = kernel_get_current_process();
if (current == NULL) {
kernel_panic("There is no current process!");
}
dbg_print("Forking '%s'(%d) process...\n", current->name, current->pid);
// Create a new task_struct.
// TODO: process is IN USER SPACE! it should be in KERNEL SPACE!
task_struct *new_process = kmalloc(sizeof(task_struct));
// TODO: this is NOT a deep copy. should a deep copy be used here?
memcpy(new_process, current, sizeof(task_struct));
// Set the id of the process.
new_process->pid = get_new_pid();
// Set the statistics of the process.
new_process->se.prio = DEFAULT_PRIO;
new_process->se.start_runtime = 0;
new_process->se.exec_start = 0;
new_process->se.sum_exec_runtime = 0;
// TODO: vruntime should be the scheduled highest values so far.
new_process->se.vruntime = current->se.vruntime;
// Create a new stack segment.
new_process->mm = create_process_image(DEFAULT_STACK_SIZE);
char *stack = (char *)new_process->mm->start_stack;
// Copy the father's stack.
memcpy((char *)new_process->mm->start_stack,
(char *)current->mm->start_stack, DEFAULT_STACK_SIZE);
// Set the base address of the stack.
char *ebp = stack + DEFAULT_STACK_SIZE; // TODO: da controllare
// Create a pointer to keep track of the top of the stack.
char *esp = stack + (r->useresp - current->mm->start_stack);
// Set the top address of the stack.
new_process->thread.useresp = (uintptr_t)esp;
// Set the base address of the stack.
new_process->thread.ebp = (uintptr_t)ebp;
// Set the program counter.
new_process->thread.eip = r->eip;
// Set the base registers.
new_process->thread.eax = 0;
new_process->thread.ebx = r->ebx;
new_process->thread.ecx = r->ecx;
new_process->thread.edx = r->edx;
// Enable the interrupts.
new_process->thread.eflags = new_process->thread.eflags | EFLAG_IF;
// Set the state of the process as running.
new_process->state = TASK_RUNNING;
// Set current as parent for the new process
new_process->parent = current;
// Initialize the list_head.
list_head_init(&new_process->run_list);
// Initialize the children list_head.
list_head_init(&new_process->children);
// Initialize the children list_head.
list_head_init(&new_process->sibling);
// Set the new_process as child of current.
list_head_add_tail(&current->children, &new_process->sibling);
// Active the new process.
enqueue_task(new_process);
dbg_print("--------------------------------------------------\n");
dbg_print("- %s process (PID: %d, eflags: %d)\n", new_process->name,
new_process->pid, new_process->thread.eflags);
dbg_print("\teip : 0x%p\n", new_process->thread.eip);
dbg_print("\tebp : 0x%p\n", new_process->thread.ebp);
dbg_print("\tesp : 0x%p\n", new_process->thread.useresp);
dbg_print("\tStack : 0x%p\n", new_process->mm->start_stack);
dbg_print("\tRunList: 0x%p\n", &new_process->run_list);
dbg_print("--------------------------------------------------\n");
dbg_print("Fork of '%s' (child pid: %d) process completed.\n",
current->name, current->pid);
// Return PID of child process to parent.
return new_process->pid;
}
static inline int push_args_on_stack(uintptr_t *esp, char *args[],
char ***argsptr)
{
int argc = 0;
char *args_ptr[256];
// Count the number of arguments.
while (args[argc] != NULL) {
++argc;
}
// Push terminating NULL.
PUSH_ON_STACK((*esp), char *, (char *)NULL);
// Prepare args with space for the terminating NULL.
for (int i = argc - 1; i >= 0; --i) {
for (int j = strlen(args[i]); j >= 0; --j) {
PUSH_ON_STACK((*esp), char, args[i][j]);
}
args_ptr[i] = (char *)(*esp);
}
// Push terminating NULL.
PUSH_ON_STACK((*esp), char *, (char *)NULL);
// Push array of pointers to the arguments.
for (int i = argc - 1; i >= 0; --i) {
PUSH_ON_STACK((*esp), char *, args_ptr[i]);
}
(*argsptr) = (char **)(*esp);
return argc;
}
int sys_execve(pt_regs *r)
{
char **argv, **_argv, **envp, **_envp;
// Check the current process.
task_struct *current = kernel_get_current_process();
if (current == NULL) {
kernel_panic("There is no current process!");
}
// Get the filename.
uintptr_t *filename = (uintptr_t *)r->ebx;
if (filename == NULL) {
return -1;
}
// Get the arguments.
argv = (char **)r->ecx;
// Get the environment.
envp = (char **)r->edx;
// Check the argument and that at least the name is provided.
if ((argv == NULL) || (argv[0] == NULL)) {
return -1;
}
// Check that the environment is provided.
if (envp == NULL) {
kernel_panic("You must provide at least an empty list for envp!");
}
// Set the name.
strcpy(current->name, argv[0]);
// Set the top address of the stack.
current->thread.useresp = (uintptr_t)current->thread.ebp;
// Set the program counter.
current->thread.eip = (uintptr_t)filename;
int argc = push_args_on_stack(&current->thread.useresp, argv, &_argv);
push_args_on_stack(&current->thread.useresp, envp, &_envp);
PUSH_ON_STACK(current->thread.useresp, char **, _envp);
PUSH_ON_STACK(current->thread.useresp, char **, _argv);
PUSH_ON_STACK(current->thread.useresp, int, argc);
PUSH_ON_STACK(current->thread.useresp, uintptr_t, (uintptr_t)exit_handler);
// dbg_print("_ARGV:0x%09x {\n", _argv);
// for (int i = 0; _argv[i] != NULL; ++i) {
// dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]);
// }
// dbg_print("}\n");
//
// if (_envp != NULL) {
// dbg_print("_ENVP:0x%09x {\n", _envp);
// for (int i = 0; _envp[i] != NULL; ++i) {
// dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]);
// }
// dbg_print("}\n");
// }
// Perform the switch to the new process.
do_switch(current, r);
dbg_print("Executing '0x%p' for process %d with %d arguments (0x%p)...\n",
filename, current->pid, argc, argv);
return 0;
}
+383
View File
@@ -0,0 +1,383 @@
/// MentOS, The Mentoring Operating system project
/// @file scheduler.c
/// @brief Scheduler structures and functions.
/// @copyright (c) 2019 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "scheduler.h"
#include "tss.h"
#include "fpu.h"
#include "prio.h"
#include "wait.h"
#include "kheap.h"
#include "panic.h"
#include "debug.h"
#include "clock.h"
#include "errno.h"
#include "rbtree.h"
#include "stdlib.h"
#include "list_head.h"
/// @brief Assembly function setting the kernel stack to jump into
/// location in Ring 3 mode (USER mode).
/// @param location The location where to jump.
/// @param stack The stack to use.
extern void enter_userspace(uintptr_t location, uintptr_t stack);
/// The list of processes.
runqueue_t runqueue;
uint32_t get_new_pid(void)
{
/// The current unused PID.
static unsigned long int tid = 1;
// Return the pid and increment.
return tid++;
}
task_struct *kernel_get_current_process()
{
return runqueue.curr;
}
task_struct *kernel_get_running_process(pid_t pid)
{
list_head *it;
list_for_each (it, &runqueue.queue) {
task_struct *entry = list_entry(it, task_struct, run_list);
if (entry != NULL) {
if (entry->pid == pid) {
return entry;
}
}
}
return NULL;
}
size_t kernel_get_active_processes()
{
return runqueue.num_active;
}
void kernel_initialize_scheduler()
{
// Initialize the runqueue list of tasks.
list_head_init(&runqueue.queue);
// Reset the current task.
runqueue.curr = NULL;
// Reset the number of active tasks.
runqueue.num_active = 0;
}
void enqueue_task(task_struct *process)
{
// If current_process is NULL, then process is the current process.
if (runqueue.curr == NULL) {
runqueue.curr = process;
}
// Add the new process at the end.
list_head_add_tail(&process->run_list, &runqueue.queue);
// Increment the number of active processes.
++runqueue.num_active;
}
void dequeue_task(task_struct *process)
{
// Delete the process from the list of running processes.
list_head_del(&process->run_list);
// Decrement the number of active processes.
--runqueue.num_active;
}
void kernel_schedule(pt_regs *f)
{
// Check if there is a running process.
if (runqueue.curr == NULL) {
return;
}
//==== Update Statistics ===================================================
time_t delta_exec = get_millisecond() - runqueue.curr->se.exec_start;
// dbg_print("[%3d] %d = %d - %d\n", runqueue.curr->pid, delta_exec,
// get_millisecond(), runqueue.curr->se.exec_start);
// set the sum_exec_runtime
runqueue.curr->se.sum_exec_runtime += delta_exec;
//==========================================================================
//==== Handle Zombies ======================================================
task_struct *next_process = NULL;
if (runqueue.curr->state == EXIT_ZOMBIE) {
// get the next process after the current one
list_head *nNode = runqueue.curr->run_list.next;
// check if we reached the head of list_head
if (nNode == &runqueue.queue)
nNode = nNode->next;
// get the task_struct
next_process = list_entry(nNode, task_struct, run_list);
// Remove the zombie task.
dequeue_task(runqueue.curr);
} else {
//==== Scheduling ======================================================
// Pointer to the next process to be executed.
next_process = pick_next_task(&runqueue, delta_exec);
//======================================================================
}
//==========================================================================
// Print, for debugging purpose, data about the current process.
if (runqueue.num_active > 2) {
dbg_print("PID:%3d, PRIO:%3d, VRUNTIME:%9d, SUM_EXEC:%9d\n",
next_process->pid, next_process->se.prio,
next_process->se.vruntime, next_process->se.sum_exec_runtime);
}
//==== Context switch ======================================================
// Update the context of the current process.
update_context(f, runqueue.curr);
// Check if the next and current processes are different.
if (next_process != runqueue.curr) {
// Copy into Kernel stack the next process's context.
do_switch(next_process, f);
runqueue.curr->se.sum_exec_runtime = get_millisecond();
// Update the last context switch time of the next process.
next_process->se.exec_start = get_millisecond();
}
//==========================================================================
// Update the start execution time if it is executed for the first time
if (next_process->se.start_runtime == 0)
next_process->se.start_runtime = get_millisecond();
}
void update_context(pt_regs *f, task_struct *process)
{
// Store the registers.
process->thread.gs = f->gs;
process->thread.fs = f->fs;
process->thread.es = f->es;
process->thread.ds = f->ds;
process->thread.edi = f->edi;
process->thread.esi = f->esi;
process->thread.ebp = f->ebp;
process->thread.ebx = f->ebx;
process->thread.edx = f->edx;
process->thread.ecx = f->ecx;
process->thread.eax = f->eax;
process->thread.eip = f->eip;
process->thread.eflags = f->eflags;
process->thread.useresp = f->useresp;
// TODO: Check if the following registers should be saved.
// process->thread.cs = f->cs;
// process->thread.ss = f->ss;
// Store the FPU.
switch_fpu();
}
void do_switch(task_struct *process, pt_regs *f)
{
// Switch to the next process.
runqueue.curr = process;
// Restore the registers.
f->gs = process->thread.gs;
f->fs = process->thread.fs;
f->es = process->thread.es;
f->ds = process->thread.ds;
f->edi = process->thread.edi;
f->esi = process->thread.esi;
f->ebp = process->thread.ebp;
f->ebx = process->thread.ebx;
f->edx = process->thread.edx;
f->ecx = process->thread.ecx;
f->eax = process->thread.eax;
f->eip = process->thread.eip;
f->eflags = process->thread.eflags;
f->useresp = process->thread.useresp;
// TODO: Check if the following registers should be restored.
// f->cs = process->thread.cs;
// f->ss = process->thread.ss;
// Restore the FPU.
unswitch_fpu();
}
int set_user_nice(task_struct *p, long nice)
{
if (PRIO_TO_NICE(p->se.prio) != nice && nice >= MIN_NICE &&
nice <= MAX_NICE) {
p->se.prio = NICE_TO_PRIO(nice);
}
return PRIO_TO_NICE(p->se.prio);
}
void enter_user_jmp(uintptr_t location, uintptr_t stack)
{
// Reset stack pointer for kernel.
tss_set_stack(0x10, initial_esp);
// update start execution time.
runqueue.curr->se.start_runtime = get_millisecond();
// last context switch time.
runqueue.curr->se.exec_start = get_millisecond();
// Jump in location.
enter_userspace(location, stack);
}
pid_t sys_getpid()
{
// Get the current task.
if (runqueue.curr == NULL) {
kernel_panic("There is no current process!");
}
// Return the process identifer of the process.
return runqueue.curr->pid;
}
pid_t sys_getppid()
{
// Get the current task.
if (runqueue.curr == NULL) {
kernel_panic("There is no current process!");
}
if (runqueue.curr->parent == NULL) {
return 0;
}
// Return the parent process identifer of the process.
return runqueue.curr->parent->pid;
}
int sys_nice(int increment)
{
// Get the current task.
if (runqueue.curr == NULL) {
kernel_panic("There is no current process!");
}
if (increment < -40) {
increment = -40;
}
if (increment > 40) {
increment = 40;
}
int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment;
dbg_print("New nice value would be : %d\n", newNice);
if (newNice < MIN_NICE) {
newNice = MIN_NICE;
}
if (newNice > MAX_NICE) {
newNice = MAX_NICE;
}
int actualNice = set_user_nice(runqueue.curr, newNice);
dbg_print("Actual new nice value is: %d\n", actualNice);
return actualNice;
}
pid_t sys_waitpid(pid_t pid, int *status, int options)
{
// Get the current task.
if (runqueue.curr == NULL) {
kernel_panic("There is no current process!");
}
/* For now we do not support waiting for processes inside the given
* process group (pid < -1).
*/
if ((pid < -1) || (pid == 0)) {
errno = ESRCH;
return (-1);
}
if (pid == runqueue.curr->pid) {
errno = ECHILD;
return (-1);
}
if (options != 0 && options != WNOHANG) {
errno = EINVAL;
return (-1);
}
if (status == NULL) {
errno = EFAULT;
return (-1);
}
list_head *it;
list_for_each (it, &runqueue.curr->children) {
task_struct *entry = list_entry(it, task_struct, sibling);
if (entry == NULL) {
continue;
}
if (entry->state != EXIT_ZOMBIE) {
continue;
}
if ((pid > 1) && (entry->pid != pid)) {
continue;
}
// Save the pid to return.
pid_t ppid = entry->pid;
// Save the state.
(*status) = entry->state; //TODO: da rivedere
// Remove entry from children of parent.
list_head_del(&entry->sibling);
// Delete the task_struct.
kfree(entry);
dbg_print("Freeing memory of process %d.\n", ppid);
return ppid;
}
return 0;
}
void sys_exit(int exit_code)
{
// Get the current task.
if (runqueue.curr == NULL) {
kernel_panic("There is no current process!");
}
task_struct *init_proc = kernel_get_running_process(1);
if (runqueue.curr == init_proc) {
kernel_panic("Init process cannot call sys_exit!");
}
// Set the termination code of the process.
runqueue.curr->exit_code = (exit_code << 8) & 0xFF00;
// Set the state of the process to zombie.
runqueue.curr->state = EXIT_ZOMBIE;
// If it has children, then init process has to take care of them.
if (!list_head_empty(&runqueue.curr->children)) {
dbg_print("Moving children of %s(%d) to init(%d): {\n",
runqueue.curr->name, runqueue.curr->pid, init_proc->pid);
// TODO: Try to plug the list of children instead of iterating.
list_head *it;
list_for_each (it, &runqueue.curr->children) {
task_struct *entry = list_entry(it, task_struct, sibling);
dbg_print(" [%d] %s\n", entry->pid, entry->name);
it = entry->sibling.prev;
list_head_del(&entry->sibling);
list_head_add_tail(&init_proc->children, &entry->sibling);
entry->parent = init_proc;
}
dbg_print("}\n");
dbg_print("Listing children of init(%d): {\n", init_proc->pid);
list_for_each (it, &init_proc->children) {
task_struct *entry = list_entry(it, task_struct, sibling);
dbg_print(" [%d] %s\n", entry->pid, entry->name);
}
dbg_print("}\n");
}
// Free the space occupied by the stack.
destroy_process_image(runqueue.curr->mm);
// Debugging message.
dbg_print("Process %d exited with value %d\n", runqueue.curr->pid,
exit_code);
}
+73
View File
@@ -0,0 +1,73 @@
/// @file scheduler_algorithm.c
/// @brief Round Robin algorithm.
/// @date Mar 2019.
#include "prio.h"
#include "debug.h"
#include "assert.h"
#include "list_head.h"
#include "scheduler.h"
#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))]
#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO)
task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec)
{
// Pointer to the next task to schedule.
task_struct *next = NULL;
#if defined(SCHEDULER_RR)
//==== Implementatin of the Round-Robin Scheduling algorithm ============
//=======================================================================
#elif defined(SCHEDULER_PRIORITY)
//==== Implementatin of the Priority Scheduling algorithm ===============
// get the first element of the list
next = list_entry(/*...*/);
// Get its static priority.
time_t min = /*...*/
list_head *it;
// Inter over the runqueue to find the task with the smallest priority value
list_for_each (it, &runqueue->queue) {
task_struct *entry = list_entry(/*...*/);
// Check entry has a lower priority
if (/*...*/) {
/*...*/
}
}
//=======================================================================
#elif defined(SCHEDULER_CFS)
//==== Implementatin of the Completely Fair Scheduling ==================
// Get the weight of the current process.
// (use GET_WEIGHT macro!)
int weight = /*...*/
if (weight != NICE_0_LOAD) {
// get the multiplicative factor for its delta_exec.
double factor = /*...*/
// weight the delta_exec with the multiplicative factor.
delta_exec = // ...
}
// Update vruntime of the current process.
// ...
// Inter over the runqueue to find the task with the smallest vruntime value
// ...
//========================================================================
#else
#error "You should enable a scheduling algorithm!"
#endif
assert(next && "No valid task selected. Have you implemented a scheduling algorithm?");
return next;
}
+82
View File
@@ -0,0 +1,82 @@
; MentOS, The Mentoring Operating system project
; @file user.asm
; @brief
; @copyright (c) 2019 This file is distributed under the MIT License.
; See LICENSE.md for details.
; Enter userspace (ring3) (from Ring 0, namely Kernel)
; Usage: enter_userspace(uintptr_t location, uintptr_t stack);
; On stack
; | stack |
; | location |
; | return address |
; | EBP | EBP
; | SS |
; | ESP |
; | EFLAGS |
; | CS |
; | EIP |
global enter_userspace ; Allows the C code to call enter_userspace(...).
enter_userspace:
push ebp ; Save current ebp
mov ebp, esp ; open a new stack frame
;==== Segment selector =====================================================
mov ax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; we don't need to worry about SS. it's handled by iret
;---------------------------------------------------------------------------
; We have to prepare the following stack before executing iret
; SS --> Segment selector
; ESP --> Stack address
; EFLAGS --> CPU state flgas
; CS --> Code segment
; EIP --> Entry point
;
;==== User data segmenet with bottom 2 bits set for ring3 ?=================
push 0x23 ; push SS on Kernel's stack
;---------------------------------------------------------------------------
;==== (ESP) Stack address ==================================================
mov eax, [ebp + 0xC] ; get uintptr_t stack
push eax ; push process's stack address on Kernel's stack
;---------------------------------------------------------------------------
;==== (EFLAGS) =============================================================
pushf ; push EFLAGS into Kernel's stack
pop eax ; pop EFLAGS into eax
or eax, 0x200 ; enable interrupt ?request ring3
push eax ; push new EFLAGS on Kernel's stack
;---------------------------------------------------------------------------
;==== (CS) Code Segment ====================================================
push 0x1B ;
;---------------------------------------------------------------------------
;==== (EIP) Entry point ====================================================
mov eax, [ebp + 0x8] ; get uintptr_t location
push eax ; push uintptr_t location on Kernel's stack
;---------------------------------------------------------------------------
iret ; interrupt return
; WE SHOULD NOT STILL BE HERE! :(
;==== Reset segment selector ===============================================
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
;---------------------------------------------------------------------------
add esp, 0x14 ; reset stack pointer (20 bytes)
pop ebp ; reset value of ebp
ret ; return to kernel code