maybe i'm close to fixing multitasking

This commit is contained in:
2025-05-16 14:55:19 +03:00
parent 4fbaed321c
commit 2e372ae9c4
3 changed files with 209 additions and 195 deletions
+52 -9
View File
@@ -3,6 +3,17 @@
#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
{
Ready,
@@ -13,22 +24,54 @@ typedef enum TaskState
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
{
uint32_t pid; // Process ID
TaskState state; // Process state
uintptr_t programCounter; // Program Counter
uintptr_t* stackBase; // Top of the stack
uintptr_t* stackPointer; // Stack Pointer
uint32_t flags; // Status/Flags register
struct Process* next; // Pointer to the next task in the list
uint32_t* page_directory;
TaskState state; // Состояние процесса
char* kstack; // Стек ядра
Context* context; // Контекст для переключения
TrapFrame* tf; // Trap frame (только для ring 3)
uint32_t ring; // Уровень привилегий (0 или 3)
struct Process* next; // Следующий процесс
uint32_t* page_directory; // Директория страниц
} 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 schedule();
void scheduler_lock();
void scheduler_unlock();
#endif
+36 -89
View File
@@ -1,117 +1,64 @@
extern current
global switchProcess
struc PCB
.PID resd 1 ; Offset: 0
.STATE resd 1 ; Offset: 4
.PROGRAM_COUNTER resd 1 ; Offset: 8
.STACK_BASE resd 1 ; Offset: 12
.STACK_POINTER resd 1 ; Offset: 16
.KSTACK resd 1 ; Offset: 8
.CONTEXT resd 1 ; Offset: 12
.TF resd 1 ; Offset: 16
.FLAGS resd 1 ; Offset: 20
.NEXT resd 1 ; Offset: 24
.PAGE_DIRECTORY resd 1 ; Offset: 28
endstruc
section .text
; void switchProcess(Process* next)
switchProcess:
; __________Stack___________
; |------------------------|
; | Return Address (EIP) | +0
; |------------------------|
; | Process* next | +4
; |------------------------|
section .text
global switchProcess
extern current
extern trapret
; Push volatile registers (per the 32-bit System V ABI)
switchProcess:
; Сохраняем регистры текущего процесса
push ebx
push esi
push edi
push ebp
; __________Stack___________
; |------------------------|
; | EBP | +0
; |------------------------|
; | EDI | +4
; |------------------------|
; | ESI | +8
; |------------------------|
; | EBX | +12
; |-------- ---------------|
; | Return Address (EIP) | +16
; |------------------------|
; | Process* next | +20
; |------------------------|
; Save current process' state
; Сохраняем указатель стека в current->context
mov edi, [current]
; EDI now contains the pointer to [Process* current]
; |-----|
; | EDI | <== [Process* current]
; |-----|
mov [edi + 12], esp ; PCB.CONTEXT находится по смещению 12
; Set [current->PCB.STACK_POINTER] to the value in ESP
; Process* current->PCB.STACK_POINTER <== ESP
mov [edi + PCB.STACK_POINTER], esp
; Load next process' state
; ESP has been decremented by 16, so instead of +4,
; 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 esi, [esp + 20] ; next (первый аргумент)
mov [current], esi
mov esp, [esi + 12] ; Загружаем esp из next->context
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
; |------------------------|
; Проверяем ring процесса
mov eax, [esi + 20] ; PCB.RING находится по смещению 20
cmp eax, 3
je .ring3
ret ; Для ring 0 просто возвращаемся
.ring3:
jmp trapret ; Для ring 3 переходим через trapret
ret
trapret:
; Восстанавливаем общие регистры из trap frame
popal ; edi, esi, ebp, oesp, ebx, edx, ecx, eax
add esp, 4 ; Пропускаем oesp (не используется)
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
; Восстанавливаем сегментные регистры
pop gs
pop fs
pop es
pop ds
; and eax, ~0x80000000
; mov cr0, eax
; Пропускаем trapno и err
add esp, 8 ; trapno (4 байта) + err (4 байта)
; 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
; Теперь ESP указывает на eip, cs, eflags, esp, ss
iret
+55 -31
View File
@@ -11,50 +11,77 @@ uint32_t irq_disable_counter = 0;
uint32_t pid_counter;
uint32_t task_count;
#define STACK_SIZE 4096
#define KSTACKSIZE 4096
#define USTACKSIZE 4096
extern void trapret(void);
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()
{
printf("startup\n");
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->state = Ready;
p->programCounter = 0;
p->stackBase = heap_alloc(STACK_SIZE);
p->stackPointer = (uintptr_t*)((uint32_t)p->stackBase + STACK_SIZE);
p->ring = ring;
//put the arguments in stack
for(int i = arg_count-1; i >=0; i--)
*(--p->stackPointer) = (uintptr_t)args[i];
// Выделяем стек ядра
p->kstack = alloc_page();//heap_alloc(KSTACKSIZE);
uint32_t* sp = (uint32_t*)(p->kstack + KSTACKSIZE);
// Добавляем адрес возврата (фиктивный, для выравнивания)
*(--p->stackPointer) = 0; // Фиктивный адрес возврата (не используется напрямую)
if (ring == DPL_USER) {
// Настраиваем 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;
*(--p->stackPointer) = (uintptr_t)processStartup;
*(--p->stackPointer) = 0;//ebp
*(--p->stackPointer) = 0;//edi
*(--p->stackPointer) = 0;//esi
*(--p->stackPointer) = 0;//ebx
// Добавляем адрес возврата для trapret
sp -= 1;
*sp = (uint32_t)trapret;
} else {
// Для ring 0 не используем trap frame
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->next = 0;
if(!queue)
{
if (!queue) {
queue = p;
current = queue;
}
else
{
} else {
Process* curr = queue;
while (curr->next)
curr = curr->next;
@@ -64,6 +91,7 @@ Process* task_create(EntryPoint func, void** args, uint32_t arg_count)
return p;
}
// Остальные функции остаются без изменений
void scheduler_lock()
{
asm volatile("cli");
@@ -109,22 +137,18 @@ void task1(int arg1, char* 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()
{
jump_usermode2();
//we create this task two times because in other case it just won't start
task_create((EntryPoint)&idle, NULL, 0);
task_create((EntryPoint)&idle, NULL, 0);
task_create((EntryPoint)idle, NULL, 0, 0);
task_create((EntryPoint)idle, NULL, 0, 0);
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};
task_create((EntryPoint)&task2, args2, 2);
task_create((EntryPoint)task2, args2, 2, 0);
}
#include "../include/string.h"