maybe i'm close to fixing multitasking
This commit is contained in:
+62
-19
@@ -3,32 +3,75 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Сегменты для ядра и пользователя
|
||||||
|
#define SEG_KCODE 1 // Сегмент кода ядра
|
||||||
|
#define SEG_KDATA 2 // Сегмент данных ядра
|
||||||
|
#define SEG_UCODE 3 // Сегмент кода пользователя
|
||||||
|
#define SEG_UDATA 4 // Сегмент данных пользователя
|
||||||
|
#define DPL_KERNEL 0 // Уровень привилегий ring 0
|
||||||
|
#define DPL_USER 3 // Уровень привилегий ring 3
|
||||||
|
|
||||||
|
// Флаги процессора
|
||||||
|
#define FL_IF 0x202 // Разрешить прерывания
|
||||||
|
|
||||||
typedef enum TaskState
|
typedef enum TaskState
|
||||||
{
|
{
|
||||||
Ready,
|
Ready,
|
||||||
Running,
|
Running,
|
||||||
Waiting,
|
Waiting,
|
||||||
Terminated
|
Terminated
|
||||||
} TaskState;
|
} TaskState;
|
||||||
|
|
||||||
typedef void (*EntryPoint)(void);
|
typedef void (*EntryPoint)(void);
|
||||||
|
|
||||||
|
// Структура контекста для переключения между процессами
|
||||||
|
typedef struct Context {
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t eip;
|
||||||
|
} Context;
|
||||||
|
|
||||||
|
// Структура trap frame для перехода в ring 3
|
||||||
|
typedef struct TrapFrame {
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t oesp;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t eax;
|
||||||
|
uint16_t gs;
|
||||||
|
uint16_t fs;
|
||||||
|
uint16_t es;
|
||||||
|
uint16_t ds;
|
||||||
|
uint32_t trapno;
|
||||||
|
uint32_t err;
|
||||||
|
uint32_t eip;
|
||||||
|
uint32_t cs;
|
||||||
|
uint32_t eflags;
|
||||||
|
uint32_t esp;
|
||||||
|
uint32_t ss;
|
||||||
|
} TrapFrame;
|
||||||
|
|
||||||
typedef struct Process
|
typedef struct Process
|
||||||
{
|
{
|
||||||
uint32_t pid; // Process ID
|
uint32_t pid; // Process ID
|
||||||
TaskState state; // Process state
|
TaskState state; // Состояние процесса
|
||||||
uintptr_t programCounter; // Program Counter
|
char* kstack; // Стек ядра
|
||||||
uintptr_t* stackBase; // Top of the stack
|
Context* context; // Контекст для переключения
|
||||||
uintptr_t* stackPointer; // Stack Pointer
|
TrapFrame* tf; // Trap frame (только для ring 3)
|
||||||
uint32_t flags; // Status/Flags register
|
uint32_t ring; // Уровень привилегий (0 или 3)
|
||||||
struct Process* next; // Pointer to the next task in the list
|
struct Process* next; // Следующий процесс
|
||||||
|
uint32_t* page_directory; // Директория страниц
|
||||||
uint32_t* page_directory;
|
|
||||||
} Process;
|
} Process;
|
||||||
|
|
||||||
Process* task_create(EntryPoint func, void** args, uint32_t arg_count);
|
Process* task_create(EntryPoint func, void** args, uint32_t arg_count, uint32_t ring);
|
||||||
void scheduler_init();
|
void scheduler_init();
|
||||||
void schedule();
|
void schedule();
|
||||||
void scheduler_lock();
|
void scheduler_lock();
|
||||||
void scheduler_unlock();
|
void scheduler_unlock();
|
||||||
#endif
|
|
||||||
|
#endif
|
||||||
+53
-106
@@ -1,117 +1,64 @@
|
|||||||
extern current
|
|
||||||
global switchProcess
|
|
||||||
|
|
||||||
struc PCB
|
struc PCB
|
||||||
.PID resd 1 ; Offset: 0
|
.PID resd 1 ; Offset: 0
|
||||||
.STATE resd 1 ; Offset: 4
|
.STATE resd 1 ; Offset: 4
|
||||||
.PROGRAM_COUNTER resd 1 ; Offset: 8
|
.KSTACK resd 1 ; Offset: 8
|
||||||
.STACK_BASE resd 1 ; Offset: 12
|
.CONTEXT resd 1 ; Offset: 12
|
||||||
.STACK_POINTER resd 1 ; Offset: 16
|
.TF resd 1 ; Offset: 16
|
||||||
.FLAGS resd 1 ; Offset: 20
|
.FLAGS resd 1 ; Offset: 20
|
||||||
.NEXT resd 1 ; Offset: 24
|
.NEXT resd 1 ; Offset: 24
|
||||||
|
.PAGE_DIRECTORY resd 1 ; Offset: 28
|
||||||
endstruc
|
endstruc
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
|
|
||||||
; void switchProcess(Process* next)
|
; void switchProcess(Process* next)
|
||||||
|
section .text
|
||||||
|
global switchProcess
|
||||||
|
extern current
|
||||||
|
extern trapret
|
||||||
|
|
||||||
switchProcess:
|
switchProcess:
|
||||||
; __________Stack___________
|
; Сохраняем регистры текущего процесса
|
||||||
; |------------------------|
|
push ebx
|
||||||
; | Return Address (EIP) | +0
|
push esi
|
||||||
; |------------------------|
|
push edi
|
||||||
; | Process* next | +4
|
push ebp
|
||||||
; |------------------------|
|
|
||||||
|
|
||||||
; Push volatile registers (per the 32-bit System V ABI)
|
; Сохраняем указатель стека в current->context
|
||||||
push ebx
|
mov edi, [current]
|
||||||
push esi
|
mov [edi + 12], esp ; PCB.CONTEXT находится по смещению 12
|
||||||
push edi
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
; __________Stack___________
|
; Загружаем следующий процесс
|
||||||
; |------------------------|
|
mov esi, [esp + 20] ; next (первый аргумент)
|
||||||
; | EBP | +0
|
mov [current], esi
|
||||||
; |------------------------|
|
mov esp, [esi + 12] ; Загружаем esp из next->context
|
||||||
; | EDI | +4
|
|
||||||
; |------------------------|
|
|
||||||
; | ESI | +8
|
|
||||||
; |------------------------|
|
|
||||||
; | EBX | +12
|
|
||||||
; |-------- ---------------|
|
|
||||||
; | Return Address (EIP) | +16
|
|
||||||
; |------------------------|
|
|
||||||
; | Process* next | +20
|
|
||||||
; |------------------------|
|
|
||||||
|
|
||||||
; Save current process' state
|
; Восстанавливаем регистры
|
||||||
mov edi, [current]
|
pop ebp
|
||||||
; EDI now contains the pointer to [Process* current]
|
pop edi
|
||||||
; |-----|
|
pop esi
|
||||||
; | EDI | <== [Process* current]
|
pop ebx
|
||||||
; |-----|
|
|
||||||
|
|
||||||
; Set [current->PCB.STACK_POINTER] to the value in ESP
|
; Проверяем ring процесса
|
||||||
; Process* current->PCB.STACK_POINTER <== ESP
|
mov eax, [esi + 20] ; PCB.RING находится по смещению 20
|
||||||
mov [edi + PCB.STACK_POINTER], esp
|
cmp eax, 3
|
||||||
|
je .ring3
|
||||||
|
ret ; Для ring 0 просто возвращаемся
|
||||||
|
.ring3:
|
||||||
|
jmp trapret ; Для ring 3 переходим через trapret
|
||||||
|
|
||||||
|
trapret:
|
||||||
|
; Восстанавливаем общие регистры из trap frame
|
||||||
|
popal ; edi, esi, ebp, oesp, ebx, edx, ecx, eax
|
||||||
|
add esp, 4 ; Пропускаем oesp (не используется)
|
||||||
|
|
||||||
|
; Восстанавливаем сегментные регистры
|
||||||
|
pop gs
|
||||||
|
pop fs
|
||||||
|
pop es
|
||||||
|
pop ds
|
||||||
|
|
||||||
|
; Пропускаем trapno и err
|
||||||
|
add esp, 8 ; trapno (4 байта) + err (4 байта)
|
||||||
|
|
||||||
|
|
||||||
; Load next process' state
|
; Теперь ESP указывает на eip, cs, eflags, esp, ss
|
||||||
; ESP has been decremented by 16, so instead of +4,
|
iret
|
||||||
; we need to do +20. 4 pushes, 1 return address, 4
|
|
||||||
; bytes each.
|
|
||||||
mov esi, [esp + 20]
|
|
||||||
; ESI now contains the pointer to [Process* next]
|
|
||||||
; __________Stack___________
|
|
||||||
; |------------------------|
|
|
||||||
; | Return Address (EIP) | +16
|
|
||||||
; |-----| |------------------------|
|
|
||||||
; | ESI | <== | Process* next | +20
|
|
||||||
; |-----| |------------------------|
|
|
||||||
|
|
||||||
|
|
||||||
; Set [EXTERN Process* current] to the value in ESI
|
|
||||||
; Process* current <== ESI (Process* next)
|
|
||||||
mov [current], esi
|
|
||||||
|
|
||||||
mov esp, [esi + PCB.STACK_POINTER]
|
|
||||||
; |-----| |-----|
|
|
||||||
; | ESP | <== | ESI |
|
|
||||||
; |-----| |-----|
|
|
||||||
|
|
||||||
; Restore non-volatile registers (per the 32-bit System V ABI)
|
|
||||||
pop ebp
|
|
||||||
pop edi
|
|
||||||
pop esi
|
|
||||||
pop ebx
|
|
||||||
|
|
||||||
; The stack is now restored to its original state.
|
|
||||||
; __________Stack___________
|
|
||||||
; |------------------------|
|
|
||||||
; | Return Address (EIP) | +0
|
|
||||||
; |------------------------|
|
|
||||||
; | Process* next | +4
|
|
||||||
; |------------------------|
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
global jump_usermode
|
|
||||||
extern test_user_function
|
|
||||||
jump_usermode:
|
|
||||||
mov ax, (4 * 8) | 3 ; ring 3 data with bottom 2 bits set for ring 3
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov fs, ax
|
|
||||||
mov gs, ax ; SS is handled by iret
|
|
||||||
|
|
||||||
; and eax, ~0x80000000
|
|
||||||
; mov cr0, eax
|
|
||||||
|
|
||||||
; set up the stack frame iret expects
|
|
||||||
mov eax, esp
|
|
||||||
push (4 * 8) | 3 ; data selector
|
|
||||||
push eax ; current esp
|
|
||||||
pushf ; eflags
|
|
||||||
push (3 * 8) | 3 ; code selector (ring 3 code with bottom 2 bits set for ring 3)
|
|
||||||
push test_user_function ; instruction address to return to
|
|
||||||
iret
|
|
||||||
+94
-70
@@ -11,120 +11,144 @@ uint32_t irq_disable_counter = 0;
|
|||||||
uint32_t pid_counter;
|
uint32_t pid_counter;
|
||||||
uint32_t task_count;
|
uint32_t task_count;
|
||||||
|
|
||||||
#define STACK_SIZE 4096
|
#define KSTACKSIZE 4096
|
||||||
|
#define USTACKSIZE 4096
|
||||||
|
|
||||||
|
extern void trapret(void);
|
||||||
extern void switchProcess(Process* next);
|
extern void switchProcess(Process* next);
|
||||||
|
extern Process* queue;
|
||||||
|
extern Process* current;
|
||||||
|
extern uint32_t pid_counter;
|
||||||
|
extern uint32_t task_count;
|
||||||
|
|
||||||
|
// Функция запуска процесса в ядре, после которой произойдёт переход в ring 3
|
||||||
static void processStartup()
|
static void processStartup()
|
||||||
{
|
{
|
||||||
printf("startup\n");
|
printf("startup\n");
|
||||||
scheduler_unlock();
|
scheduler_unlock();
|
||||||
|
// Возврат из этой функции приведёт к вызову trapret
|
||||||
}
|
}
|
||||||
|
|
||||||
Process* task_create(EntryPoint func, void** args, uint32_t arg_count)
|
Process* task_create(EntryPoint func, void** args, uint32_t arg_count, uint32_t ring)
|
||||||
{
|
{
|
||||||
Process* p = heap_alloc(sizeof(Process));
|
Process* p = alloc_page();//heap_alloc(sizeof(Process));
|
||||||
p->pid = ++pid_counter;
|
p->pid = ++pid_counter;
|
||||||
p->state = Ready;
|
p->state = Ready;
|
||||||
p->programCounter = 0;
|
p->ring = ring;
|
||||||
p->stackBase = heap_alloc(STACK_SIZE);
|
|
||||||
p->stackPointer = (uintptr_t*)((uint32_t)p->stackBase + STACK_SIZE);
|
|
||||||
|
|
||||||
//put the arguments in stack
|
// Выделяем стек ядра
|
||||||
for(int i = arg_count-1; i >=0; i--)
|
p->kstack = alloc_page();//heap_alloc(KSTACKSIZE);
|
||||||
*(--p->stackPointer) = (uintptr_t)args[i];
|
uint32_t* sp = (uint32_t*)(p->kstack + KSTACKSIZE);
|
||||||
|
|
||||||
// Добавляем адрес возврата (фиктивный, для выравнивания)
|
if (ring == DPL_USER) {
|
||||||
*(--p->stackPointer) = 0; // Фиктивный адрес возврата (не используется напрямую)
|
// Настраиваем trap frame для ring 3
|
||||||
|
sp -= sizeof(TrapFrame) / 4;
|
||||||
|
p->tf = (TrapFrame*)sp;
|
||||||
|
p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
|
||||||
|
p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
|
||||||
|
p->tf->es = p->tf->ds;
|
||||||
|
p->tf->fs = p->tf->ds;
|
||||||
|
p->tf->gs = p->tf->ds;
|
||||||
|
p->tf->ss = p->tf->ds;
|
||||||
|
p->tf->eflags = FL_IF;
|
||||||
|
p->tf->eip = (uint32_t)func;
|
||||||
|
p->tf->esp = USTACKSIZE; // Предполагаем, что пользовательский стек настроен
|
||||||
|
|
||||||
*(--p->stackPointer) = (uintptr_t)func;
|
// Добавляем адрес возврата для trapret
|
||||||
*(--p->stackPointer) = (uintptr_t)processStartup;
|
sp -= 1;
|
||||||
*(--p->stackPointer) = 0;//ebp
|
*sp = (uint32_t)trapret;
|
||||||
*(--p->stackPointer) = 0;//edi
|
} else {
|
||||||
*(--p->stackPointer) = 0;//esi
|
// Для ring 0 не используем trap frame
|
||||||
*(--p->stackPointer) = 0;//ebx
|
p->tf = NULL;
|
||||||
|
}
|
||||||
|
// Настраиваем начальный контекст
|
||||||
|
sp -= 5; // Место для edi, esi, ebx, ebp, eip
|
||||||
|
p->context = (Context*)sp;
|
||||||
|
p->context->edi = 0;
|
||||||
|
p->context->esi = 0;
|
||||||
|
p->context->ebx = 0;
|
||||||
|
p->context->ebp = 0;
|
||||||
|
if (ring == DPL_USER) {
|
||||||
|
p->context->eip = (uint32_t)processStartup;
|
||||||
|
} else {
|
||||||
|
p->context->eip = (uint32_t)func; // Прямо в ring 0
|
||||||
|
}
|
||||||
|
|
||||||
p->page_directory = kernel_page_directory;
|
p->page_directory = kernel_page_directory;
|
||||||
|
|
||||||
p->next = 0;
|
p->next = 0;
|
||||||
|
|
||||||
if(!queue)
|
if (!queue) {
|
||||||
{
|
queue = p;
|
||||||
queue = p;
|
current = queue;
|
||||||
current = queue;
|
} else {
|
||||||
}
|
Process* curr = queue;
|
||||||
else
|
while (curr->next)
|
||||||
{
|
curr = curr->next;
|
||||||
Process* curr = queue;
|
curr->next = p;
|
||||||
while(curr->next)
|
}
|
||||||
curr = curr->next;
|
task_count++;
|
||||||
curr->next = p;
|
return p;
|
||||||
}
|
|
||||||
task_count++;
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Остальные функции остаются без изменений
|
||||||
void scheduler_lock()
|
void scheduler_lock()
|
||||||
{
|
{
|
||||||
asm volatile("cli");
|
asm volatile("cli");
|
||||||
irq_disable_counter++;
|
irq_disable_counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler_unlock()
|
void scheduler_unlock()
|
||||||
{
|
{
|
||||||
irq_disable_counter--;
|
irq_disable_counter--;
|
||||||
if(irq_disable_counter == 0)
|
if (irq_disable_counter == 0)
|
||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
}
|
}
|
||||||
|
|
||||||
void schedule()
|
void schedule()
|
||||||
{
|
{
|
||||||
if(!current)
|
if (!current)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Process* next = current->next;
|
Process* next = current->next;
|
||||||
if(!next)
|
if (!next)
|
||||||
next = queue;
|
next = queue;
|
||||||
if(next != current)
|
if (next != current)
|
||||||
{
|
{
|
||||||
//loadPageDirectory(next->page_directory);
|
//loadPageDirectory(next->page_directory);
|
||||||
switchProcess(next);
|
switchProcess(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void idle()
|
void idle()
|
||||||
{
|
{
|
||||||
while(1){}
|
while (1) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void task1(int arg1, char* arg2)
|
void task1(int arg1, char* arg2)
|
||||||
{
|
{
|
||||||
while(1){
|
while (1) {
|
||||||
if(arg1 == 42)
|
if (arg1 == 42)
|
||||||
printf("task1 0x%x %s\n", arg1, arg2);
|
printf("task1 0x%x %s\n", arg1, arg2);
|
||||||
else
|
else
|
||||||
printf("ZHOPA %s\n", arg2);
|
printf("ZHOPA %s\n", arg2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void task2(int arg1, int arg2)
|
void task2(int arg1, int arg2)
|
||||||
{
|
{
|
||||||
while(1){printf("task2 %X %X\n", arg1, arg2);}
|
while (1) { arg1++; printf("task2 %X %X\n", arg1, arg2); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void jump_usermode2(void);
|
|
||||||
|
|
||||||
void scheduler_init()
|
void scheduler_init()
|
||||||
{
|
{
|
||||||
jump_usermode2();
|
task_create((EntryPoint)idle, NULL, 0, 0);
|
||||||
//we create this task two times because in other case it just won't start
|
task_create((EntryPoint)idle, NULL, 0, 0);
|
||||||
task_create((EntryPoint)&idle, NULL, 0);
|
|
||||||
task_create((EntryPoint)&idle, NULL, 0);
|
|
||||||
|
|
||||||
void* args1[] = {(void*)42, (void*)"ebalo"};
|
void* args1[] = {(void*)42, (void*)"ebalo"};
|
||||||
task_create((EntryPoint)&task1, args1, 2);
|
task_create((EntryPoint)task1, args1, 2, 0);
|
||||||
void* args2[] = {(void*)69, (void*)420};
|
void* args2[] = {(void*)69, (void*)420};
|
||||||
task_create((EntryPoint)&task2, args2, 2);
|
task_create((EntryPoint)task2, args2, 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "../include/string.h"
|
#include "../include/string.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user