Add scheduler (non preemptif) with context switch + add close primitive to the VFS

This commit is contained in:
Yann MAGNIN 2020-01-14 18:41:28 +01:00
parent fc7a6e04ea
commit 223f92b6b2
26 changed files with 420 additions and 175 deletions

View File

@ -79,6 +79,9 @@ struct dentry
// Internal inode
void *inode;
// Internal counter used by opne() and close()
uint32_t counter;
// Internal file informations
mode_t mode;

View File

@ -9,10 +9,10 @@
#include <kernel/types.h>
#define PROCESS_NB_OPEN_FILE (4)
#define PROCESS_USER_STACK_SIZE (15 * 1024)
#define PROCESS_USER_STACK_SIZE (2 * 1024)
#define PROCESS_KERNEL_STACK_SIZE (512)
#define PROCESS_NAME_LENGHT (16)
#define PROCESS_MAX (3)
#define PROCESS_MAX (4)
#define PROC_IDLE (0)
@ -26,12 +26,12 @@ struct process
uint32_t user;
} stack;
// Process name.
char name[PROCESS_NAME_LENGHT];
// Context management
common_context_t context;
// Process name.
char name[PROCESS_NAME_LENGHT];
// Open file management
struct {
enum {

View File

@ -0,0 +1,73 @@
#ifndef __KERNEL_SHEDULER_H__
# define __KERNEL_SHEDULER_H__
#include <stddef.h>
#include <stdint.h>
#include <kernel/process.h>
// Define the number of task
#define SCHED_TASK_NB_MAX PROCESS_MAX
#define SCHED_QUANTUM_TICKS (4)
// Internal struct used by the sheduler
struct sched_task
{
// Process informations
struct process *process;
// Internal scheduler part
// @note: defined here to facilite the
// management in assembly
struct sched_task *next;
// Task status
enum {
SCHED_RUNNING,
SCHED_SLEEPING,
SCHED_DOWN,
SCHED_STOPPED,
SCHED_ZOMBIE
} status;
// Preemptif part
struct {
uint8_t _static;
int8_t _dynamic;
} priority;
};
//---
// Internal function
//---
/*
** Initialize scheduler cache
** @note: only called once by the kernel.
*/
extern void sched_initialize(void);
/*
** Add new process to sheduler
*/
extern int sched_add_task(struct process *process);
/*
** Start shedluler (in theory, this function is called only
** time by the kernel during "bootstrap" part).
*/
extern void sched_start(void);
/*
** Save current context of the current task and run the
** next one based on internal task priority.
*/
extern void sched_schedule(void);
/*
** Internal function wich will switch current process
** context with the next context
*/
extern void sched_context_switch(common_context_t *current, common_context_t *next);
#endif /*__KERNEL_SHEDULER_H__*/

View File

@ -1,47 +0,0 @@
#ifndef __KERNEL_SHEDULER_H__
# define __KERNEL_SHEDULER_H__
#include <stddef.h>
#include <stdint.h>
// Internal struct used by the sheduler
struct shed_task
{
// Task status
enum {
RUNNING,
SLEEPING,
DOWN,
STOPPED,
ZOMBIE
} status;
// Process informations
struct process_s process;
// Preemptif part
int8_t priority;
};
//---
// Internal function
//---
/*
** Add new process to sheduler
*/
extern int shed_add_task(struct process_s *process);
/*
** Start shedluler (in theory, this function is called only
** time by the kernel during "bootstrap" part).
*/
extern void shed_start(void);
/*
** Save current context of the current task and run the
** next one based on internal task priority.
*/
extern void shed_shedule(void);
#endif /*__KERNEL_SHEDULER_H__*/

View File

@ -1,54 +0,0 @@
.text
.global _kernel_switch
.type _kernel_switch, @function
.align 2
_kernel_switch:
! Save process context into unbakable register
mov r4, r8
! Update SR register to block
! interrupt / exception and
! switch register bank to simalate
! system call.
mov.l .sr_msk, r1 ! get mask for SR.BL = 1, SR.RB = 1 and SR.IMASK = 0b1111
stc sr, r0 ! get SR register
or r1, r0 ! set mask for BL and IMASK
ldc r0, sr ! update SR regsiter
! set process context into bankable register
! because unbankable register will be over-written.
mov r8, r0
! Load first process
ldc.l @r0+, R0_BANK ! set "process" r0 regsiter
ldc.l @r0+, R1_BANK ! set "process" r1 regsiter
ldc.l @r0+, R2_BANK ! set "process" r2 regsiter
ldc.l @r0+, R3_BANK ! set "process" r3 regsiter
ldc.l @r0+, R4_BANK ! set "process" r4 regsiter
ldc.l @r0+, R5_BANK ! set "process" r5 regsiter
ldc.l @r0+, R6_BANK ! set "process" r6 regsiter
ldc.l @r0+, R7_BANK ! set "process" r7 regsiter
mov.l @r0+, r8 ! set r8 regsiter
mov.l @r0+, r9 ! set r9 regsiter
mov.l @r0+, r10 ! set r10 regsiter
mov.l @r0+, r11 ! set r11 regsiter
mov.l @r0+, r12 ! set r12 regsiter
mov.l @r0+, r13 ! set r13 regsiter
mov.l @r0+, r14 ! set r14 regsiter
mov.l @r0+, r15 ! set r15 register
ldc.l @r0+, gbr ! set gbr regsiter
lds.l @r0+, macl ! set macl regsiter
lds.l @r0+, mach ! set mach regsiter
ldc.l @r0+, ssr ! set ssr regsiter
ldc.l @r0+, spc ! set spc regsiter
lds.l @r0+, pr ! set pr regsiter
! Process switch
rte
nop
.align 4
.sr_msk: .long 0x700000f0
.end

View File

@ -1,10 +1,11 @@
#include <stdint.h>
#include <stddef.h>
#include <kernel/types.h>
#include <kernel/context.h>
#include <kernel/atomic.h>
#include <kernel/types.h>
#include <kernel/process.h>
#include <kernel/syscall.h>
#include <kernel/scheduler.h>
#include <kernel/util.h>
#include <kernel/fs/vfs.h>
#include <kernel/fs/stat.h>
@ -161,26 +162,12 @@ int start(void)
// Test mode !
extern void kernel_test(void);
//extern void kernel_test(void);
//kernel_test();
//---
// Start first process !
//---
//TODO: initialize sheduler !!
atomic_start();
// Test process switch
/*__asm__ volatile (
"ldc %0, ssr;"
"ldc %1, spc;"
"rte;"
"nop;"
:
: "r"(0x50000000), "r"(&kernel_test)
:
);*/
// Create first process: Vhex.
struct process *vhex_process = process_create("Vhex");
@ -194,9 +181,6 @@ int start(void)
while (1) { __asm__ volatile ("sleep"); }
}
// Initialize CPU configuration for the process.
vhex_process->context.ssr = 0x40000000;
// Load programe.
//vhex_process->context.spc = (uint32_t)&kernel_test;
vhex_process->context.spc = (uint32_t)loader("/mnt/smemfs/VHEX/shell.elf", vhex_process);
@ -227,31 +211,19 @@ int start(void)
}
}
// Test context
/*__asm__ volatile (
"ldc %0, ssr;"
"ldc %1, spc;"
"mov %2, r15;"
"rte;"
"nop;"
:
: "r"(vhex_process->context.ssr),
"r"(&kernel_test),
"r"(vhex_process->context.reg[15])
:
);*/
// DEBUG !
kvram_clear();
printk(0, 0, "Initialize scheduler !");
printk(0, 1, "Try to start sceduler...");
kvram_display();
DBG_WAIT;
// Set the first process
// TODO: send the process to the sheduler !!
extern struct process *process_current;
process_current = vhex_process;
// Initialize sheduler !!
sched_initialize();
sched_add_task(vhex_process);
sched_start();
// Switch to first process.
kernel_switch(&vhex_process->context);
// normally the kernel SHOULD not
// arrive here.
// normally the kernel SHOULD / CAN not arrive here.
kvram_clear();
kvram_print(0, 0, "Kernel job fini !");
kvram_display();

View File

@ -3,6 +3,12 @@
int sys_close(int fd)
{
//TODO !!!!
return (-1);
extern struct process *process_current;
// Check fd
if (fd < 0 || fd >= PROCESS_NB_OPEN_FILE)
return (-1);
// call VFS close primitive
return (vfs_close(&process_current->opfile[fd].file));
}

View File

@ -0,0 +1,14 @@
#include <kernel/fs/vfs.h>
int vfs_close(FILE *file)
{
// Check error
if (file == NULL)
return (-1);
//TODO: call close primitive for device !
// Update internal dentry counter
((struct dentry*)file->private)->counter += 1;
return (0);
}

View File

@ -29,7 +29,7 @@ int vfs_open(FILE *file, char const *pathname, int flags)
return (-2);
}
// debug
// Debug
kvram_clear();
printk(0, 0, "vfs_open(): inode found !");
printk(0, 1, "path: %s", pathname);
@ -39,10 +39,11 @@ int vfs_open(FILE *file, char const *pathname, int flags)
kvram_display();
DBG_WAIT;
//TODO: update interne dentry counter !!
// Update interne dentry counter
dentry->counter = dentry->counter + 1;
// Initialize new file.
file->private = dentry->inode;
file->private = dentry;
file->permission = dentry->mode & (~__S_IFMT);
file->file_op = dentry->dentry_op.file_op;
file->cursor = 0;

View File

@ -17,7 +17,7 @@ ssize_t vfs_read(FILE *file, void *buf, size_t count)
// Read with FS specifique primitive and return the numbe of reading bytes.
memset(buf, 0x00, count);
ssize_t read = file->file_op->read(file->private, buf, count, file->cursor);
ssize_t read = file->file_op->read(((struct dentry*)file->private)->inode, buf, count, file->cursor);
if (read != -1)
file->cursor = file->cursor + read;
return (read);

View File

@ -16,7 +16,7 @@ ssize_t vfs_write(FILE *file, const void *buf, size_t count)
return (-1);
// Writa with FS specifique primitive and return the numbe of reading bytes.
ssize_t write = file->file_op->write(file->private, buf, count, file->cursor);
ssize_t write = file->file_op->write(((struct dentry*)file->private)->inode, buf, count, file->cursor);
if (write != -1)
file->cursor = file->cursor + write;
return (write);

View File

@ -0,0 +1,52 @@
#include <kernel/scheduler.h>
#include <kernel/atomic.h>
static struct sched_task *sched_alloc(void)
{
extern struct sched_task sched_task_cache[SCHED_TASK_NB_MAX];
int i;
i = SCHED_TASK_NB_MAX;
while (--i >= 0)
{
if (sched_task_cache[i].process == NULL)
return (&sched_task_cache[i]);
}
return (NULL);
}
int sched_add_task(struct process *process)
{
extern struct sched_task *sched_task_queue;
struct sched_task *sched_slot;
// Check error
if (process == NULL)
return (-1);
// Start atomic operations
atomic_start();
// Try to find free slot
sched_slot = sched_alloc();
if (sched_slot == NULL)
{
atomic_stop();
return (-2);
}
// Initialize new task
// TODO: update quantum ticks management
sched_slot->priority._static = SCHED_QUANTUM_TICKS;
sched_slot->priority._dynamic = 0;
sched_slot->process = process;
sched_slot->status = SCHED_RUNNING;
// Register task into scheduler task queue
sched_slot->next = sched_task_queue;
sched_task_queue = sched_slot;
// Stop atomic operation
atomic_stop();
return (0);
}

View File

@ -0,0 +1,107 @@
.text
.global _sched_context_switch
.type _sched_context_switch, @function
.align 2
/*
** void sched_context_switch(common_context_t *current, common_context_t *next);
**
** @note:
** r4 - current scheduler task
** r5 - next task
** r0 - tmp
*/
_sched_context_switch:
! Save contexts arg
mov.l r8, @-r15 ! save r8
mov.l r9, @-r15 ! save r9
mov r4, r8 ! save current context into unbankable reg
mov r5, r9 ! save next context into unbankable reg
! Update SR register to block interrupt / exception and
! switch register bank to simalate system call.
! @note:
! Normaly this is the TMU0 interrupt so the interrupt
! and register banck is already switchted BUT durring the
! bootstrap part, the kernel call this function in "user"
! context. This is why we force update CPU configuration.
mov.l .sr_msk, r1 ! get mask for SR.BL = 1, SR.RB = 1 and SR.IMASK = 0b1111
stc sr, r0 ! get SR register
or r1, r0 ! set mask for BL, RB and IMASK
ldc r0, sr ! update SR regsiter
! Restore context arg
mov r8, r4 ! restore current context
mov r9, r5 ! restore next context
mov.l @r15+, r9 ! restore r9
mov.l @r15+, r8 ! restore r8
save_current_context:
! Check current process
! @note: current process can be NULL !
tst r4, r4 ! if current task == NULL...
bt context_load ! ...if yes, jump at <context_switch>
add #88, r4 ! get &process->context (end)
! save current context
sts.l pr, @-r4 ! get pr regsiter
stc.l spc, @-r4 ! get spc regsiter
stc.l ssr, @-r4 ! get ssr regsiter
sts.l mach, @-r4 ! get mach regsiter
sts.l macl, @-r4 ! get macl regsiter
stc.l gbr, @-r4 ! get gbr regsiter
mov.l r15, @-r4 ! get r15 register
mov.l r14, @-r4 ! get r14 regsiter
mov.l r13, @-r4 ! get r13 regsiter
mov.l r12, @-r4 ! get r12 regsiter
mov.l r11, @-r4 ! get r11 regsiter
mov.l r10, @-r4 ! get r10 regsiter
mov.l r9, @-r4 ! get r9 regsiter
mov.l r8, @-r4 ! get r8 regsiter
stc.l R7_BANK, @-r4 ! get "process" r7 regsiter
stc.l R6_BANK, @-r4 ! get "process" r6 regsiter
stc.l R5_BANK, @-r4 ! get "process" r5 regsiter
stc.l R4_BANK, @-r4 ! get "process" r4 regsiter
stc.l R3_BANK, @-r4 ! get "process" r3 regsiter
stc.l R2_BANK, @-r4 ! get "process" r2 regsiter
stc.l R1_BANK, @-r4 ! get "process" r1 regsiter
stc.l R0_BANK, @-r4 ! get "process" r0 regsiter
context_load:
! Load next process
! @note: next process can not be NULL !
ldc.l @r5+, R0_BANK ! set "process" r0 regsiter
ldc.l @r5+, R1_BANK ! set "process" r1 regsiter
ldc.l @r5+, R2_BANK ! set "process" r2 regsiter
ldc.l @r5+, R3_BANK ! set "process" r3 regsiter
ldc.l @r5+, R4_BANK ! set "process" r4 regsiter
ldc.l @r5+, R5_BANK ! set "process" r5 regsiter
ldc.l @r5+, R6_BANK ! set "process" r6 regsiter
ldc.l @r5+, R7_BANK ! set "process" r7 regsiter
mov.l @r5+, r8 ! set r8 regsiter
mov.l @r5+, r9 ! set r9 regsiter
mov.l @r5+, r10 ! set r10 regsiter
mov.l @r5+, r11 ! set r11 regsiter
mov.l @r5+, r12 ! set r12 regsiter
mov.l @r5+, r13 ! set r13 regsiter
mov.l @r5+, r14 ! set r14 regsiter
mov.l @r5+, r15 ! set r15 register
ldc.l @r5+, gbr ! set gbr regsiter
lds.l @r5+, macl ! set macl regsiter
lds.l @r5+, mach ! set mach regsiter
ldc.l @r5+, ssr ! set ssr regsiter
ldc.l @r5+, spc ! set spc regsiter
lds.l @r5+, pr ! set pr regsiter
! Process switch
rte ! use RTE to switch process
nop ! (db) nop
.align 4
.sr_msk: .long 0x700000f0
.end

View File

@ -0,0 +1,32 @@
#include <kernel/scheduler.h>
// Internal task queue
struct sched_task sched_task_cache[SCHED_TASK_NB_MAX];
struct sched_task *sched_task_current;
struct sched_task *sched_task_queue;
struct process *process_current;
void sched_initialize(void)
{
// Initialize internal scheduler cache
for (int i = 0 ; i < SCHED_TASK_NB_MAX ; i = i + 1)
{
sched_task_queue[i].status = SCHED_DOWN;
sched_task_queue[i].process = NULL;
sched_task_queue[i].priority._static = 0;
sched_task_queue[i].priority._dynamic = 0;
sched_task_queue[i].next = NULL;
}
// Initialize internal process informations
// @note:
// * process_sched_current Used by the kernel to handle
// interrupt / exception
// * process_user_current Used by the kernel to allow
// interaction with the Video RAM
// and the screen driver.
// TODO: use internal vram for each processus ?
sched_task_current = NULL;
sched_task_queue = NULL;
process_current = NULL;
}

View File

@ -5,8 +5,8 @@
struct process *process_create(const char *name)
{
extern struct dentry *vfs_root_node;
extern struct process *process_current;
extern struct dentry *vfs_root_node;
struct process *process;
pid_t process_pid;
@ -83,7 +83,7 @@ struct process *process_create(const char *name)
process->context.gbr = 0x00000000;
process->context.macl = 0x00000000;
process->context.mach = 0x00000000;
process->context.ssr = 0x00000000;
process->context.ssr = 0x40000000; // <- force privilegied mode !
process->context.spc = 0x00000000;
// Initialise file cache

View File

@ -3,7 +3,6 @@
// Create all internal global
// used to handle process.
struct process_stack process_stack[PROCESS_MAX];
struct process *process_current;
__attribute__((constructor))
void process_constructor(void)
@ -13,7 +12,4 @@ void process_constructor(void)
{
process_stack[i].status = PROC_IDLE;
}
// No process is currently running.
process_current = NULL;
}

View File

@ -0,0 +1,70 @@
#include <kernel/scheduler.h>
#include <kernel/atomic.h>
#include <kernel/util.h>
//TODO: assembly !
void sched_schedule(void)
{
extern struct sched_task *sched_task_current;
extern struct sched_task *sched_task_queue;
struct sched_task *task_current;
struct sched_task *task_next;
common_context_t *context_current;
common_context_t *context_next;
// Start atomic operation
atomic_start();
// Check current task
if (sched_task_current == NULL)
{
task_current = NULL;
task_next = (sched_task_queue != NULL)
? sched_task_queue
: NULL;
} else {
task_current = sched_task_current;
task_next = (sched_task_current->next != NULL)
? sched_task_current->next
: sched_task_queue;
}
// Check potantial error
if (task_next == NULL || task_next == sched_task_current)
{
atomic_stop();
return;
}
//TODO: check process status !!!
// Update internal scheduler task cursor
sched_task_current = task_next;
// Get context
context_current =
(task_current != NULL)
? &task_current->process->context
: NULL;
context_next =
(task_next != NULL)
? &task_next->process->context
: NULL;
// DEBUG !
kvram_clear();
printk(0, 0, "Scheduler_schudele !");
printk(0, 1, "task current = %p", task_current);
printk(0, 2, "task next = %p", task_next);
printk(0, 3, "context current = %p", context_current);
printk(0, 4, "context next = %p", context_next);
kvram_display();
DBG_WAIT;
printk(0, 5, "context switch !");
kvram_display();
// Context switch
sched_context_switch(context_current, context_next);
}

View File

@ -0,0 +1,12 @@
#include <kernel/scheduler.h>
void sched_start(void)
{
extern struct sched_task *sched_task_queue;
extern struct process *process_current;
//TODO: get CPU frequency !
//TODO: setup TMU0 interrupt !
process_current = sched_task_queue->process;
sched_schedule();
}

View File

@ -27,6 +27,11 @@ SECTIONS
.rodata : {
*(.rodata);
*(.rodata.*);
/* Builtin */
_bbuiltin_section = ALIGN(4) ;
*(.builtin);
_ebuiltin_section = . ;
}
.data ALIGN(4) : {

View File

@ -52,11 +52,11 @@ int main(void)
input[cmd_size - 1] = '\0';
// Check buit-in.
//if (check_builtin(input) != 0)
//{
if (check_builtin(input) != 0)
{
write(fd, input, cmd_size - 1);
write(fd, ": command not found\n", 20);
//}
}
}
return (0);
}

View File

@ -1,4 +1,4 @@
/*#include "util.h"
#include "util.h"
#include "builtin.h"
#include <lib/string.h>
@ -18,7 +18,12 @@ int check_builtin(char *cmd)
{
if (strcmp(list[i].name, cmd) != 0)
continue;
// Execute builtin
list[i].entry(0, NULL);
return (0);
}
return (-1);
// Create subprocess
//pid = fork();
//if (pid < 0)
@ -27,19 +32,17 @@ int check_builtin(char *cmd)
// If we are the child execute
// the builtins.
//if (pid == 0)
//{
dclear();
dprint(0, 0, "Child process !!");
dprint(0, 1, "PID = %d", getpid());
dprint(0, 2, "PPID = %d", getppid());
dupdate();
// //{
// dclear();
// dprint(0, 0, "Child process !!");
// dprint(0, 1, "PID = %d", getpid());
// dprint(0, 2, "PPID = %d", getppid());
// dupdate();
// list[i].entry(0, NULL);
return (0);
// } else {
// waitpid(pid, &wstatus, WCONTINUED);
//TODO: signal handling.
// }
// }
// return (1);
}*/
}